Haraka 3.1.4 → 3.1.6
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 +1 -1
- package/{Changes.md → CHANGELOG.md} +34 -0
- package/CONTRIBUTORS.md +26 -26
- package/README.md +68 -93
- package/SECURITY.md +178 -0
- package/bin/haraka +7 -14
- package/config/plugins +0 -3
- package/docs/Connection.md +126 -39
- package/docs/CoreConfig.md +92 -74
- package/docs/HAProxy.md +41 -25
- package/docs/Logging.md +68 -38
- package/docs/Outbound.md +124 -179
- package/docs/Plugins.md +38 -59
- package/docs/Transaction.md +78 -83
- package/docs/Tutorial.md +122 -209
- package/docs/plugins/aliases.md +1 -141
- package/docs/plugins/auth/auth_ldap.md +2 -39
- package/docs/plugins/max_unrecognized_commands.md +4 -18
- package/docs/plugins/process_title.md +3 -3
- package/docs/plugins/reseed_rng.md +11 -13
- package/docs/plugins/tls.md +7 -7
- package/docs/plugins/toobusy.md +10 -4
- package/docs/tutorials/SettingUpOutbound.md +40 -48
- package/endpoint.js +32 -2
- package/outbound/hmail.js +3 -2
- package/outbound/index.js +3 -0
- package/package.json +21 -34
- package/plugins/queue/smtp_forward.js +4 -4
- package/run_tests +3 -15
- package/server.js +17 -7
- package/smtp_client.js +8 -6
- package/test/connection.js +234 -0
- package/test/endpoint.js +32 -4
- package/test/host_pool.js +57 -31
- package/test/logger.js +75 -135
- package/test/outbound/bounce_net_errors.js +87 -131
- package/test/outbound/bounce_rfc3464.js +177 -254
- package/test/outbound/hmail.js +19 -0
- package/test/outbound/index.js +189 -0
- package/test/outbound/queue.js +92 -0
- package/test/plugins/auth/auth_base.js +39 -44
- package/test/plugins/auth/auth_vpopmaild.js +8 -9
- package/test/plugins/queue/smtp_forward.js +953 -183
- package/test/plugins/rcpt_to.host_list_base.js +58 -93
- package/test/plugins/rcpt_to.in_host_list.js +126 -175
- package/test/plugins/record_envelope_addresses.js +8 -8
- package/test/plugins/status.js +10 -10
- package/test/plugins/tls.js +9 -19
- package/test/plugins/xclient.js +75 -110
- package/test/plugins.js +10 -13
- package/test/rfc1869.js +50 -70
- package/test/server.js +438 -421
- package/test/smtp_client.js +1192 -218
- package/test/tls_socket.js +242 -0
- package/tls_socket.js +18 -22
package/README.md
CHANGED
|
@@ -1,144 +1,119 @@
|
|
|
1
|
-
|
|
1
|
+
# Haraka — a Node.js Mail Server
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
[![Coverage Status][cov-img]][cov-url]
|
|
5
5
|
|
|
6
|
-
Haraka is a highly scalable [
|
|
7
|
-
plugin architecture. Haraka can serve thousands of concurrent connections
|
|
8
|
-
and deliver thousands of messages per second. Haraka and plugins are written
|
|
9
|
-
in asynchronous JS and are very fast.
|
|
6
|
+
Haraka is a highly scalable [Node.js][1] SMTP server with a modular plugin architecture. It handles thousands of concurrent connections and delivers thousands of messages per second. Haraka and its plugins are written in asynchronous JavaScript, optimised for throughput and low latency.
|
|
10
7
|
|
|
11
|
-
Haraka
|
|
12
|
-
well as a filtering [MTA][3]. It also works well as a [MSA][5] running on
|
|
13
|
-
port 587 with auth and [dkim][6] plugins enabled.
|
|
8
|
+
Haraka offers strong spam protection (see [Plugins.md][plugins]) and is widely deployed as a filtering [MTA][3] or as a [MSA][5] on port 465 (and legacy 587) with the auth and [DKIM][6] plugins enabled.
|
|
14
9
|
|
|
15
|
-
Haraka
|
|
16
|
-
|
|
17
|
-
typically used **with** such systems.
|
|
10
|
+
Haraka is not a mail store, an [LDA][7], or an IMAP server. It is designed to work **alongside** those systems. A scalable outbound delivery engine is built in: mail flagged as `relaying` (for example, by an auth plugin) is queued for
|
|
11
|
+
outbound delivery automatically.
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
marked as `relaying` (such as via an `auth` plugin) is automatically
|
|
21
|
-
queued for outbound delivery.
|
|
13
|
+
## Plugin Architecture
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
Haraka's defining feature is its plugin system. Every SMTP transaction is a sequence of well-defined hooks — `connect`, `helo`, `mail`, `rcpt`, `data`, `data_post`, `queue`, and more — and each hook can be extended with a few lines of JavaScript. Plugins are asynchronous by default, so a slow lookup against DNS, Redis, or an HTTP API never blocks the server.
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
- [GitHub Issues][15]
|
|
17
|
+
The result is that behaviours which would require a custom MTA elsewhere are typically a small file in Haraka. For example, accepting qmail-style tagged addresses (`user-anything@domain.com`) and rewriting them to `user@domain.com` before forwarding to an Exchange or IMAP backend looks roughly like this:
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
```js
|
|
20
|
+
exports.hook_rcpt = (next, connection, params) => {
|
|
21
|
+
const rcpt = params[0]
|
|
22
|
+
const [user] = rcpt.user.split('-')
|
|
23
|
+
rcpt.user = user
|
|
24
|
+
next()
|
|
25
|
+
}
|
|
26
|
+
```
|
|
29
27
|
|
|
30
|
-
[
|
|
28
|
+
A comprehensive registry of community and core plugins — auth, DNSBLs, DKIM, SpamAssassin, rspamd, Redis, ClamAV, queue backends, and many others — lives in [Plugins.md][plugins]. To write your own, see the [plugin tutorial][tutorial].
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
## Documentation
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
- [Plugins.md][plugins] — plugin registry and configuration reference
|
|
33
|
+
- [docs/][docs] — core documentation (Connection, Transaction, Outbound, …)
|
|
34
|
+
- [Tutorial][tutorial] — step-by-step getting started guide
|
|
35
|
+
- [CHANGELOG.md][changelog] — release notes
|
|
36
|
+
- [SECURITY.md][security] — security policy and reporting
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
is providing qmail-like extended addresses to an Exchange system,
|
|
40
|
-
whereby you could receive mail as `user-anyword@domain.com`, and yet
|
|
41
|
-
still have it correctly routed to `user@domain.com`. This is a few lines of
|
|
42
|
-
code in Haraka.
|
|
38
|
+
## Getting Help
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
[
|
|
40
|
+
- [GitHub Issues][issues]
|
|
41
|
+
- [Mailing list][mailing-list] (implemented as a Haraka plugin)
|
|
42
|
+
- [Screencast: Getting started with Haraka][screencast]
|
|
46
43
|
|
|
47
|
-
|
|
44
|
+
## Installation
|
|
48
45
|
|
|
49
|
-
Haraka requires [
|
|
46
|
+
Haraka requires [Node.js][1]. Install via [npm][npm]:
|
|
50
47
|
|
|
51
48
|
```sh
|
|
52
|
-
# If the second command gives "nobody" errors, uncomment & run the next command
|
|
53
|
-
# npm -g config set user root
|
|
54
49
|
npm install -g Haraka
|
|
55
50
|
```
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### Running Haraka
|
|
60
|
-
|
|
61
|
-
First, create the service:
|
|
52
|
+
Create a service directory:
|
|
62
53
|
|
|
63
54
|
```sh
|
|
64
55
|
haraka -i /path/to/haraka_test
|
|
65
56
|
```
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
directories within. It also sets the host name used by Haraka
|
|
69
|
-
to the output of `hostname`.
|
|
58
|
+
This creates `haraka_test` with `config/` and `plugins/` subdirectories and sets the host name from `hostname(1)`. Edit `config/host_list` to add the domains for which Haraka should accept mail.
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
to receive mail addressed to `user@domain.com`, add `domain.com` to the
|
|
73
|
-
`config/host_list` file.
|
|
74
|
-
|
|
75
|
-
Finally, start Haraka using root permissions:
|
|
60
|
+
Start Haraka:
|
|
76
61
|
|
|
77
62
|
```sh
|
|
78
63
|
haraka -c /path/to/haraka_test
|
|
79
64
|
```
|
|
80
65
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
### Configure Haraka
|
|
66
|
+
## Configuration
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
overall behaviour of Haraka. By default, only messages to domains listed
|
|
87
|
-
in `config/host_list` will be accepted and then delivered via the
|
|
88
|
-
`smtp-forward` plugin. Configure the destination in `config/smtp_forward.ini`.
|
|
68
|
+
Edit `config/plugins` to select active plugins. By default, mail addressed to domains in `config/host_list` is accepted and forwarded via the `smtp-forward` plugin (configured in `config/smtp_forward.ini`).
|
|
89
69
|
|
|
90
|
-
|
|
70
|
+
Per-plugin documentation is available via:
|
|
91
71
|
|
|
92
72
|
```sh
|
|
93
|
-
haraka -h plugins
|
|
73
|
+
haraka -h plugins/<name>
|
|
94
74
|
```
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
`config/plugins`, restart Haraka and enjoy!
|
|
98
|
-
|
|
99
|
-
### Running from git
|
|
76
|
+
See [Plugins.md][plugins] for the full registry.
|
|
100
77
|
|
|
101
|
-
|
|
102
|
-
following these steps:
|
|
78
|
+
## Running from Source
|
|
103
79
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
$ npm install
|
|
112
|
-
|
|
113
|
-
Edit `config/plugins` and `config/smtp.ini` to specify the plugins and
|
|
114
|
-
config you want.
|
|
80
|
+
```sh
|
|
81
|
+
git clone https://github.com/haraka/Haraka.git
|
|
82
|
+
cd Haraka
|
|
83
|
+
npm install
|
|
84
|
+
node haraka.js
|
|
85
|
+
```
|
|
115
86
|
|
|
116
|
-
|
|
87
|
+
## Authorship and Maintenance
|
|
117
88
|
|
|
118
|
-
|
|
89
|
+
Haraka was created by [Matt Sergeant][matt-sergeant] (`baudehlo`), formerly project leader of [SpamAssassin][spamassassin] and a contributor to [Qpsmtpd][qpsmtpd]. The project is currently maintained by
|
|
90
|
+
[Matt Simerson][msimerson] (`msimerson`).
|
|
119
91
|
|
|
120
|
-
|
|
92
|
+
Haraka is the work of many hands. See [CONTRIBUTORS.md][contributors] for the full list of people who have contributed code, documentation, and plugins.
|
|
121
93
|
|
|
122
|
-
|
|
94
|
+
## License
|
|
123
95
|
|
|
124
|
-
Haraka is
|
|
125
|
-
SpamAssassin and a hacker on [Qpsmtpd][13].
|
|
96
|
+
Haraka is released under the MIT License. See [LICENSE][license] for details.
|
|
126
97
|
|
|
127
|
-
[1]:
|
|
128
|
-
[
|
|
129
|
-
[
|
|
130
|
-
[4]: https://github.com/haraka/Haraka/blob/master/Plugins.md
|
|
131
|
-
[5]: http://en.wikipedia.org/wiki/Mail_submission_agent
|
|
98
|
+
[1]: https://nodejs.org/
|
|
99
|
+
[3]: https://en.wikipedia.org/wiki/Message_transfer_agent
|
|
100
|
+
[5]: https://en.wikipedia.org/wiki/Message_submission_agent
|
|
132
101
|
[6]: https://github.com/haraka/haraka-plugin-dkim
|
|
133
102
|
[7]: https://en.wikipedia.org/wiki/Mail_delivery_agent
|
|
134
|
-
[
|
|
135
|
-
[
|
|
136
|
-
[
|
|
137
|
-
[
|
|
138
|
-
[
|
|
139
|
-
[
|
|
140
|
-
[
|
|
141
|
-
[
|
|
142
|
-
[
|
|
103
|
+
[npm]: https://www.npmjs.com/package/Haraka
|
|
104
|
+
[plugins]: https://github.com/haraka/Haraka/blob/master/Plugins.md
|
|
105
|
+
[docs]: https://github.com/haraka/Haraka/tree/master/docs
|
|
106
|
+
[tutorial]: https://github.com/haraka/Haraka/blob/master/docs/Tutorial.md
|
|
107
|
+
[changelog]: https://github.com/haraka/Haraka/blob/master/CHANGELOG.md
|
|
108
|
+
[security]: https://github.com/haraka/Haraka/blob/master/SECURITY.md
|
|
109
|
+
[contributors]: https://github.com/haraka/Haraka/blob/master/CONTRIBUTORS.md
|
|
110
|
+
[license]: https://github.com/haraka/Haraka/blob/master/LICENSE
|
|
111
|
+
[issues]: https://github.com/haraka/Haraka/issues
|
|
112
|
+
[mailing-list]: mailto:haraka-sub@harakamail.com
|
|
113
|
+
[screencast]: https://youtu.be/6twKXMAsPsw
|
|
114
|
+
[matt-sergeant]: https://github.com/baudehlo
|
|
115
|
+
[msimerson]: https://github.com/msimerson
|
|
116
|
+
[spamassassin]: https://spamassassin.apache.org/
|
|
117
|
+
[qpsmtpd]: https://github.com/smtpd/qpsmtpd/
|
|
143
118
|
[cov-img]: https://codecov.io/github/haraka/Haraka/coverage.svg
|
|
144
119
|
[cov-url]: https://codecov.io/github/haraka/Haraka?branch=master
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Security fixes are applied to the **current release** only. We encourage all users to run the latest version.
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| -------------- | --------- |
|
|
9
|
+
| 3.1.x (latest) | ✅ |
|
|
10
|
+
| < 3.1 | ❌ |
|
|
11
|
+
|
|
12
|
+
## Reporting a Vulnerability
|
|
13
|
+
|
|
14
|
+
**Please do not report security vulnerabilities through public GitHub issues.**
|
|
15
|
+
|
|
16
|
+
Use [GitHub Private Vulnerability Reporting](https://github.com/haraka/Haraka/security/advisories/new) to disclose security issues confidentially. This allows the maintainers to assess and patch the issue before public disclosure.
|
|
17
|
+
|
|
18
|
+
Include as much of the following as possible:
|
|
19
|
+
|
|
20
|
+
- A description of the vulnerability and its potential impact
|
|
21
|
+
- Steps to reproduce or a proof-of-concept
|
|
22
|
+
- Affected version(s)
|
|
23
|
+
- Any suggested mitigations or patches
|
|
24
|
+
|
|
25
|
+
## Response Process
|
|
26
|
+
|
|
27
|
+
1. **Acknowledgement** — We aim to acknowledge reports within **72 hours**.
|
|
28
|
+
2. **Assessment** — We will confirm the issue, determine severity, and identify affected versions.
|
|
29
|
+
3. **Fix & Release** — A patch release will be prepared and coordinated with the reporter.
|
|
30
|
+
4. **Disclosure** — A GitHub Security Advisory (and CVE if applicable) will be published after the fix is available.
|
|
31
|
+
|
|
32
|
+
We follow [coordinated vulnerability disclosure](https://vuls.cert.org/confluence/display/CVD). Reporters are credited in the advisory unless they prefer otherwise.
|
|
33
|
+
|
|
34
|
+
## Security Advisories
|
|
35
|
+
|
|
36
|
+
Published advisories are listed at:
|
|
37
|
+
**https://github.com/haraka/Haraka/security/advisories**
|
|
38
|
+
|
|
39
|
+
## Threat Model
|
|
40
|
+
|
|
41
|
+
Haraka is an SMTP server and plugin host. It accepts inbound network
|
|
42
|
+
connections, parses SMTP commands and message content, and can deliver mail
|
|
43
|
+
outbound when an operator enables relaying or outbound.
|
|
44
|
+
|
|
45
|
+
This threat model assumes the operator controls the host, configuration,
|
|
46
|
+
enabled plugins, credentials, TLS material, and any external services Haraka
|
|
47
|
+
is configured to use.
|
|
48
|
+
|
|
49
|
+
### Data flow
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
flowchart LR
|
|
53
|
+
client["SMTP client<br/>(untrusted)"]
|
|
54
|
+
dns["DNS resolver<br/>(untrusted)"]
|
|
55
|
+
peer["Outbound peer<br/>(untrusted)"]
|
|
56
|
+
operator(["Operator<br/>(trusted)"])
|
|
57
|
+
subgraph haraka["Haraka process — trust boundary"]
|
|
58
|
+
listener["Listener / TLS / Parser"] --> plugins["Plugin pipeline"]
|
|
59
|
+
plugins --> queue[("Queue · FS")]
|
|
60
|
+
queue --> delivery["Delivery"]
|
|
61
|
+
stores[("Config · TLS keys · Logs")]
|
|
62
|
+
end
|
|
63
|
+
client <--> listener
|
|
64
|
+
plugins <--> dns
|
|
65
|
+
delivery --> peer
|
|
66
|
+
operator -.-> stores
|
|
67
|
+
operator -.-> plugins
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The trust boundary is the Haraka process. External peers (SMTP clients, DNS
|
|
71
|
+
resolvers, outbound peers) are untrusted; data crossing the boundary inbound
|
|
72
|
+
must be validated before it affects delivery, state, or process behavior.
|
|
73
|
+
Inside the boundary, plugins, the queue, configuration, TLS material, and
|
|
74
|
+
logs share the Haraka process privilege and are the operator's
|
|
75
|
+
responsibility — they are not a security boundary against each other.
|
|
76
|
+
|
|
77
|
+
### Assets
|
|
78
|
+
|
|
79
|
+
The assets Haraka aims to protect:
|
|
80
|
+
|
|
81
|
+
- **Message content** in transit and queued on disk
|
|
82
|
+
- **AUTH credentials** received via SMTP AUTH, and any upstream credentials
|
|
83
|
+
Haraka holds for outbound or backend services
|
|
84
|
+
- **TLS private keys** and certificates used by the listener and for
|
|
85
|
+
outbound delivery
|
|
86
|
+
- **Queue integrity** — no injection, alteration, or deletion of queued
|
|
87
|
+
messages from outside the trust boundary
|
|
88
|
+
- **Availability** of the SMTP service to legitimate clients
|
|
89
|
+
- **Sender reputation** of the operator's deployment — Haraka must not be
|
|
90
|
+
abusable as an open relay or spam amplifier under documented
|
|
91
|
+
configuration
|
|
92
|
+
|
|
93
|
+
### Entry points and actors
|
|
94
|
+
|
|
95
|
+
Untrusted data enters Haraka through:
|
|
96
|
+
|
|
97
|
+
- **SMTP listeners** on the operator-configured ports (typically 25, 465,
|
|
98
|
+
587, and any additional listeners enabled by plugins or configuration)
|
|
99
|
+
- **PROXY protocol or XCLIENT metadata** when the operator has enabled
|
|
100
|
+
trusted-relay forwarding
|
|
101
|
+
- **DNS responses** to lookups performed by Haraka core or plugins
|
|
102
|
+
(rDNS, SPF, DKIM, MX, DNSBL, etc.)
|
|
103
|
+
- **Outbound SMTP responses** received from peers during delivery
|
|
104
|
+
|
|
105
|
+
Haraka distinguishes these actors:
|
|
106
|
+
|
|
107
|
+
- **Anonymous SMTP client** — connecting from the network without
|
|
108
|
+
authentication; trusted only to issue SMTP commands subject to policy
|
|
109
|
+
- **Authenticated submission user** — passed SMTP AUTH; trusted with the
|
|
110
|
+
envelope and policy the operator's auth backend permits, nothing more
|
|
111
|
+
- **Trusted relay peer** — a network source the operator has configured to
|
|
112
|
+
bypass certain checks (e.g. permitted to relay, or whose PROXY/XCLIENT
|
|
113
|
+
metadata is honored)
|
|
114
|
+
- **Operator** — controls the host, configuration, and installed plugins;
|
|
115
|
+
fully trusted
|
|
116
|
+
- **Plugin code** — runs inside the trust boundary with full process
|
|
117
|
+
privilege; treated as trusted-as-installed, and not a security boundary
|
|
118
|
+
|
|
119
|
+
### Haraka does not trust
|
|
120
|
+
|
|
121
|
+
- SMTP clients and other remote peers, including commands, envelopes, headers,
|
|
122
|
+
bodies, attachments, authentication attempts, HELO/EHLO names, and proxied
|
|
123
|
+
client metadata before validation
|
|
124
|
+
- Data returned by remote services Haraka communicates with, such as DNS
|
|
125
|
+
lookups and outbound SMTP peers
|
|
126
|
+
- Untrusted remote input must not be able to trigger behavior beyond
|
|
127
|
+
documented SMTP and plugin semantics, such as unauthorized relaying,
|
|
128
|
+
unintended command execution, protected-data disclosure, or service
|
|
129
|
+
unavailability
|
|
130
|
+
|
|
131
|
+
### Haraka trusts
|
|
132
|
+
|
|
133
|
+
- The operating system, filesystem, Node.js runtime, local network, process
|
|
134
|
+
privileges, and local administrators operating the service
|
|
135
|
+
- Haraka configuration and deployment choices, including listener exposure,
|
|
136
|
+
relay and authentication policy, TLS certificates, proxy settings, and
|
|
137
|
+
enabled plugins
|
|
138
|
+
- Code loaded as plugins or dependencies. Plugins run with Haraka's process
|
|
139
|
+
privileges and are not a security boundary
|
|
140
|
+
- Upstream services and dependencies as separate projects; flaws in those
|
|
141
|
+
components are usually reported upstream unless Haraka's integration creates
|
|
142
|
+
a distinct vulnerability
|
|
143
|
+
|
|
144
|
+
### STRIDE summary
|
|
145
|
+
|
|
146
|
+
In-scope threats are classified using [STRIDE][stride] applied at the
|
|
147
|
+
Haraka trust boundary shown above. A finding that lets a remote peer
|
|
148
|
+
cause any of the following under documented or default-safe configuration
|
|
149
|
+
is generally a vulnerability.
|
|
150
|
+
|
|
151
|
+
| Category | Example threats at the Haraka boundary |
|
|
152
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
153
|
+
| **S**poofing | Forged HELO/EHLO or AUTH, identity bypass, unvalidated XCLIENT or proxy metadata accepted as authoritative |
|
|
154
|
+
| **T**ampering | SMTP smuggling, CRLF or header injection, parser inconsistencies that change how a message is delivered or interpreted |
|
|
155
|
+
| **R**epudiation | Remote input that erases or forges the Received: trail, or otherwise defeats logging under default configuration |
|
|
156
|
+
| **I**nformation disclosure | Leakage of message content, credentials, or internal state through SMTP responses, DSNs, error messages, or logs |
|
|
157
|
+
| **D**enial of service | Deterministic resource exhaustion or crashes triggered by remote input without operator misconfiguration |
|
|
158
|
+
| **E**levation of privilege | Remote code execution, or escape of documented SMTP/plugin semantics into arbitrary behavior inside the Haraka process |
|
|
159
|
+
|
|
160
|
+
Plugins execute inside the trust boundary by design, so a malicious or
|
|
161
|
+
vulnerable plugin can produce any STRIDE category above. Findings that
|
|
162
|
+
require installing such a plugin are not vulnerabilities in Haraka itself.
|
|
163
|
+
|
|
164
|
+
[stride]: https://en.wikipedia.org/wiki/STRIDE_model
|
|
165
|
+
|
|
166
|
+
## Scope
|
|
167
|
+
|
|
168
|
+
Issues that require control of a trusted element are out of scope, including:
|
|
169
|
+
|
|
170
|
+
- Vulnerabilities requiring control of the host OS, filesystem, Node.js
|
|
171
|
+
runtime, local administrator account, or other trusted infrastructure
|
|
172
|
+
- Malicious or compromised plugins or dependencies intentionally installed or
|
|
173
|
+
enabled by the operator
|
|
174
|
+
- Pure misconfiguration of listeners, relay policy, TLS, proxying, or
|
|
175
|
+
outbound destinations unless Haraka's documented defaults create the
|
|
176
|
+
insecure state
|
|
177
|
+
|
|
178
|
+
Issues in third-party plugins maintained outside this repository should be reported to their respective maintainers.
|
package/bin/haraka
CHANGED
|
@@ -11,7 +11,6 @@ const os = require('node:os')
|
|
|
11
11
|
|
|
12
12
|
const nopt = require('nopt')
|
|
13
13
|
const utils = require('haraka-utils')
|
|
14
|
-
const sprintf = require('sprintf-js').sprintf
|
|
15
14
|
const base = path.join(__dirname, '..')
|
|
16
15
|
const ver = utils.getVersion(base)
|
|
17
16
|
const knownOpts = {
|
|
@@ -313,15 +312,7 @@ if (parsed.version) {
|
|
|
313
312
|
;(async () => {
|
|
314
313
|
const qlist = await outbound.list_queue()
|
|
315
314
|
for (const todo of qlist) {
|
|
316
|
-
console.log(
|
|
317
|
-
sprintf(
|
|
318
|
-
'Q: %s rcpt:%d from:%s domain:%s',
|
|
319
|
-
todo.file,
|
|
320
|
-
todo.rcpt_to.length,
|
|
321
|
-
todo.mail_from.toString(),
|
|
322
|
-
todo.domain,
|
|
323
|
-
),
|
|
324
|
-
)
|
|
315
|
+
console.log(`Q: ${todo.file} rcpt:${todo.rcpt_to.length} from:${todo.mail_from} domain:${todo.domain}`)
|
|
325
316
|
}
|
|
326
317
|
process.exit()
|
|
327
318
|
})()
|
|
@@ -379,12 +370,14 @@ if (parsed.version) {
|
|
|
379
370
|
console.log('')
|
|
380
371
|
for (const hook of getHooks()) {
|
|
381
372
|
if (!plugins.registered_hooks[hook]) continue
|
|
382
|
-
console.log(
|
|
383
|
-
console.log(
|
|
384
|
-
console.log(
|
|
373
|
+
console.log(`Hook: ${hook} `.padEnd(80, '-'))
|
|
374
|
+
console.log(`${'Plugin'.padEnd(35)} ${'Method'.padEnd(35)} ${'Prio'.padEnd(4)} T/O`)
|
|
375
|
+
console.log('-'.repeat(80))
|
|
385
376
|
for (let p = 0; p < plugins.registered_hooks[hook].length; p++) {
|
|
386
377
|
const item = plugins.registered_hooks[hook][p]
|
|
387
|
-
console.log(
|
|
378
|
+
console.log(
|
|
379
|
+
`${item.plugin.padEnd(35)} ${item.method.padEnd(35)} ${String(item.priority).padStart(4)} ${String(item.timeout).padStart(3)}`,
|
|
380
|
+
)
|
|
388
381
|
}
|
|
389
382
|
console.log('')
|
|
390
383
|
}
|
package/config/plugins
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
# status
|
|
13
13
|
# process_title
|
|
14
14
|
# syslog
|
|
15
|
-
# watch
|
|
16
15
|
|
|
17
16
|
# CONNECT
|
|
18
17
|
# ----------
|
|
@@ -20,7 +19,6 @@
|
|
|
20
19
|
# karma
|
|
21
20
|
# relay
|
|
22
21
|
# access
|
|
23
|
-
# p0f
|
|
24
22
|
# geoip
|
|
25
23
|
# asn
|
|
26
24
|
# fcrdns
|
|
@@ -49,7 +47,6 @@ mail_from.is_resolvable
|
|
|
49
47
|
# At least one rcpt_to plugin is REQUIRED for inbound email.
|
|
50
48
|
rcpt_to.in_host_list
|
|
51
49
|
# qmail-deliverable
|
|
52
|
-
# rcpt_to.routes
|
|
53
50
|
|
|
54
51
|
# DATA
|
|
55
52
|
# ----------
|
package/docs/Connection.md
CHANGED
|
@@ -1,66 +1,153 @@
|
|
|
1
1
|
# Connection Object
|
|
2
2
|
|
|
3
|
-
For each connection to Haraka there is one connection object.
|
|
3
|
+
For each connection to Haraka there is one connection object. It is the first argument passed to almost every plugin hook and is the primary context object plugins use to inspect and act on the SMTP session.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Properties
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### connection.uuid
|
|
8
8
|
|
|
9
|
-
A unique UUID for this connection.
|
|
9
|
+
A unique UUID for this connection. Used as the connection identifier in logs and inherited by `transaction.uuid`.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### connection.remote
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
- host - reverse DNS of the remote hosts IP
|
|
15
|
-
- is_private - true if the remote IP is from a private (loopback, RFC 1918, link local, etc.) IP address.
|
|
16
|
-
- is_local - true if the remote IP is localhost (loopback, link local)
|
|
13
|
+
Information about the host connecting to Haraka.
|
|
17
14
|
|
|
18
|
-
-
|
|
15
|
+
- `ip` — remote IP address
|
|
16
|
+
- `port` — remote TCP port
|
|
17
|
+
- `host` — reverse DNS of the remote IP (populated by the `connect.rdns_access` / `connect` hooks)
|
|
18
|
+
- `info` — free-form descriptor (e.g. populated by FCrDNS)
|
|
19
|
+
- `closed` — `true` once the remote end has dropped the connection
|
|
20
|
+
- `is_private` — `true` if the remote IP is in a private range (RFC 1918, loopback, link-local, etc.)
|
|
21
|
+
- `is_local` — `true` if the remote IP is localhost / loopback
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
- port - the port number handling the connection.
|
|
22
|
-
- host - the rDNS host name of the local IP
|
|
23
|
+
### connection.local
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
Information about the Haraka server endpoint handling this connection.
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
- `ip` — the IP of the Haraka server, as reported by the OS
|
|
28
|
+
- `port` — the port number handling the connection
|
|
29
|
+
- `host` — the primary host name of the Haraka server
|
|
30
|
+
- `info` — `Haraka` (with `/<version>` appended when `headers.show_version` is enabled in `connection.ini`)
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
### connection.hello
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
- host - The hostname given with HELO or EHLO
|
|
34
|
+
The greeting given by the client.
|
|
34
35
|
|
|
35
|
-
-
|
|
36
|
+
- `verb` — `EHLO` or `HELO`, whichever the client used
|
|
37
|
+
- `host` — the hostname argument
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
### connection.tls
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
State of the TLS layer on this connection.
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
- `enabled` — `true` once STARTTLS has been negotiated (or the listener is `smtps`)
|
|
44
|
+
- `advertised` — `true` if Haraka advertised STARTTLS in the EHLO response
|
|
45
|
+
- `verified` — `true` if the peer certificate validated against the configured CAs
|
|
46
|
+
- `cipher` — the negotiated cipher object (`name`, `version`, …)
|
|
47
|
+
- `verifyError` — the verification error, if any
|
|
48
|
+
- `peerCertificate` — the parsed peer certificate (when client certs are used)
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
### connection.proxy
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
deliver mails outbound). This is normally set by SMTP AUTH, or sometimes via
|
|
49
|
-
an IP address check.
|
|
52
|
+
Proxy-protocol state, set when the connection arrived via HAProxy (see [HAProxy.md](HAProxy.md)).
|
|
50
53
|
|
|
51
|
-
-
|
|
54
|
+
- `allowed` — `true` if the remote IP is in the `haproxy.hosts` allow-list
|
|
55
|
+
- `ip` — the proxy server's IP (the real client IP appears in `connection.remote.ip` once PROXY is parsed)
|
|
56
|
+
- `type` — currently `null` or `'haproxy'`
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
verbatim as it was sent. Can be useful in certain botnet detection techniques.
|
|
58
|
+
### connection.notes
|
|
55
59
|
|
|
56
|
-
- connection.
|
|
60
|
+
A plain object that persists for the lifetime of the connection. Use it to share state between plugin hooks. For structured per-test results prefer `connection.results`. See also [haraka-notes](https://github.com/haraka/haraka-notes).
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
### connection.results
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
Structured store for plugin results. See [haraka-results](https://github.com/haraka/haraka-results).
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
### connection.transaction
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
The current `Transaction` object. Valid between `MAIL FROM` and the end of `queue` / `RSET` (or until `MAIL FROM` is rejected). See [Transaction.md](Transaction.md).
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
### connection.relaying
|
|
71
|
+
|
|
72
|
+
Boolean. `true` if this connection is allowed to relay (i.e. deliver mail outbound). Normally set by an auth plugin or an IP allow-list. Reading or writing this property transparently routes through the current transaction when one exists, so the flag survives across multiple messages in a single connection only when set on the connection.
|
|
73
|
+
|
|
74
|
+
### connection.capabilities
|
|
75
|
+
|
|
76
|
+
Array of ESMTP capabilities advertised in the EHLO response (e.g. `['PIPELINING', '8BITMIME', 'SIZE 0', 'STARTTLS', 'AUTH PLAIN LOGIN']`). Plugins may push additional capability strings during the `capabilities` hook.
|
|
77
|
+
|
|
78
|
+
### connection.esmtp
|
|
79
|
+
|
|
80
|
+
`true` if the client used `EHLO` (as opposed to `HELO`).
|
|
81
|
+
|
|
82
|
+
### connection.pipelining
|
|
83
|
+
|
|
84
|
+
`true` once Haraka has advertised, and the client has used, SMTP pipelining.
|
|
85
|
+
|
|
86
|
+
### connection.early_talker
|
|
87
|
+
|
|
88
|
+
`true` if the client sent data before Haraka issued its banner — a
|
|
89
|
+
common spam-bot signal.
|
|
90
|
+
|
|
91
|
+
### connection.tran_count
|
|
92
|
+
|
|
93
|
+
Number of transactions completed on this connection.
|
|
94
|
+
|
|
95
|
+
### connection.rcpt_count / connection.msg_count
|
|
96
|
+
|
|
97
|
+
Per-disposition counters (`accept`, `tempfail`, `reject`) tracking
|
|
98
|
+
recipients and full messages on this connection.
|
|
99
|
+
|
|
100
|
+
### connection.start_time
|
|
101
|
+
|
|
102
|
+
Connection start time, in epoch milliseconds (`Date.now()`).
|
|
103
|
+
|
|
104
|
+
### connection.last_response
|
|
105
|
+
|
|
106
|
+
The last SMTP response line Haraka sent to the client.
|
|
107
|
+
|
|
108
|
+
### connection.last_reject
|
|
109
|
+
|
|
110
|
+
The text of the last rejection issued to this client (used by
|
|
111
|
+
`max_unrecognized_commands` and similar throttling plugins).
|
|
112
|
+
|
|
113
|
+
### connection.errors
|
|
114
|
+
|
|
115
|
+
Count of protocol errors on this connection.
|
|
116
|
+
|
|
117
|
+
### connection.current_line
|
|
118
|
+
|
|
119
|
+
Low-level. The current line as sent by the remote end, verbatim. Useful
|
|
120
|
+
for botnet fingerprinting.
|
|
121
|
+
|
|
122
|
+
### connection.state
|
|
123
|
+
|
|
124
|
+
The connection's protocol state — one of the values in `haraka-constants`'s `connection.state` table (`PAUSE`, `CMD`, `LOOP`, `DATA`, `DISCONNECTING`, `DISCONNECTED`).
|
|
125
|
+
|
|
126
|
+
## Methods
|
|
127
|
+
|
|
128
|
+
### connection.respond(code, msg, cb)
|
|
129
|
+
|
|
130
|
+
Send an SMTP response to the client. `code` is the numeric SMTP code, `msg` is the human-readable text (a string or an array of strings for a multi-line response). The callback fires when the response has been written.
|
|
131
|
+
|
|
132
|
+
### connection.disconnect()
|
|
133
|
+
|
|
134
|
+
Close the connection after running the `disconnect` hook.
|
|
135
|
+
|
|
136
|
+
### connection.reset_transaction(cb)
|
|
137
|
+
|
|
138
|
+
Tear down the current transaction (equivalent to `RSET`) and invoke `cb` when complete.
|
|
139
|
+
|
|
140
|
+
### connection.set(path, value)
|
|
141
|
+
|
|
142
|
+
Assign a nested property safely, e.g. `connection.set('remote.host', 'mx.example.com')`. Setting `remote.ip`
|
|
143
|
+
automatically recomputes `remote.is_private` / `remote.is_local`.
|
|
144
|
+
|
|
145
|
+
### connection.get(path)
|
|
146
|
+
|
|
147
|
+
Read a nested property, returning `undefined` if any segment is missing.
|
|
148
|
+
|
|
149
|
+
### connection.loginfo / lognotice / logwarn / logerror / logdebug / logcrit / logalert / logemerg / logprotocol / logdata
|
|
150
|
+
|
|
151
|
+
Log at the named level. Each takes either `(msg)` or `(plugin, msg, data)`.
|
|
152
|
+
|
|
153
|
+
See [Logging.md](Logging.md).
|