haraka 0.0.33 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.githooks/pre-commit +41 -0
  3. package/.prettierignore +6 -0
  4. package/.qlty/.gitignore +7 -0
  5. package/.qlty/configs/.shellcheckrc +1 -0
  6. package/.qlty/qlty.toml +15 -0
  7. package/CHANGELOG.md +1894 -0
  8. package/CLAUDE.md +40 -0
  9. package/CONTRIBUTORS.md +34 -0
  10. package/Dockerfile +50 -0
  11. package/GEMINI.md +38 -0
  12. package/LICENSE +22 -0
  13. package/Plugins.md +227 -0
  14. package/README.md +119 -4
  15. package/SECURITY.md +178 -0
  16. package/TODO +22 -0
  17. package/address.js +53 -0
  18. package/bin/haraka +593 -0
  19. package/bin/haraka_grep +32 -0
  20. package/config/aliases +2 -0
  21. package/config/auth_flat_file.ini +7 -0
  22. package/config/auth_vpopmaild.ini +9 -0
  23. package/config/connection.ini +79 -0
  24. package/config/delay_deny.ini +7 -0
  25. package/config/dhparams.pem +8 -0
  26. package/config/host_list +3 -0
  27. package/config/host_list_regex +6 -0
  28. package/config/http.ini +11 -0
  29. package/config/lmtp.ini +7 -0
  30. package/config/log.ini +11 -0
  31. package/config/me +1 -0
  32. package/config/outbound.bounce_message +18 -0
  33. package/config/outbound.bounce_message_html +36 -0
  34. package/config/outbound.bounce_message_image +106 -0
  35. package/config/outbound.ini +24 -0
  36. package/config/plugins +67 -0
  37. package/config/smtp.ini +37 -0
  38. package/config/smtp_bridge.ini +4 -0
  39. package/config/smtp_forward.ini +31 -0
  40. package/config/smtp_proxy.ini +27 -0
  41. package/config/tarpit.timeout +1 -0
  42. package/config/tls.ini +83 -0
  43. package/config/tls_cert.pem +23 -0
  44. package/config/tls_key.pem +28 -0
  45. package/config/watch.ini +12 -0
  46. package/config/xclient.hosts +2 -0
  47. package/connection.js +1863 -0
  48. package/contrib/Haraka.cf +6 -0
  49. package/contrib/Haraka.pm +35 -0
  50. package/contrib/bad_smtp_server.pl +25 -0
  51. package/contrib/bsd-rc.d/haraka +61 -0
  52. package/contrib/debian-init.d/haraka +87 -0
  53. package/contrib/haraka.init +96 -0
  54. package/contrib/haraka.service +23 -0
  55. package/contrib/plugin2npm.sh +81 -0
  56. package/contrib/ubuntu-upstart/haraka.conf +27 -0
  57. package/coverage/coverage-final.json +2 -0
  58. package/coverage/coverage-summary.json +33 -0
  59. package/coverage/tmp/coverage-79131-1779241025146-0.json +1 -0
  60. package/coverage/tmp/coverage-79132-1779240999690-0.json +1 -0
  61. package/coverage/tmp/coverage-79172-1779241000095-0.json +1 -0
  62. package/coverage/tmp/coverage-79210-1779241000156-0.json +1 -0
  63. package/coverage/tmp/coverage-79211-1779241000209-0.json +1 -0
  64. package/coverage/tmp/coverage-79212-1779241000266-0.json +1 -0
  65. package/coverage/tmp/coverage-79213-1779241000441-0.json +1 -0
  66. package/coverage/tmp/coverage-79214-1779241000626-0.json +1 -0
  67. package/coverage/tmp/coverage-79215-1779241000795-0.json +1 -0
  68. package/coverage/tmp/coverage-79216-1779241000965-0.json +1 -0
  69. package/coverage/tmp/coverage-79218-1779241001013-0.json +1 -0
  70. package/coverage/tmp/coverage-79219-1779241001179-0.json +1 -0
  71. package/coverage/tmp/coverage-79220-1779241006249-0.json +1 -0
  72. package/coverage/tmp/coverage-79227-1779241011453-0.json +1 -0
  73. package/coverage/tmp/coverage-79229-1779241011537-0.json +1 -0
  74. package/coverage/tmp/coverage-79230-1779241011647-0.json +1 -0
  75. package/coverage/tmp/coverage-79231-1779241011765-0.json +1 -0
  76. package/coverage/tmp/coverage-79232-1779241011841-0.json +1 -0
  77. package/coverage/tmp/coverage-79233-1779241011909-0.json +1 -0
  78. package/coverage/tmp/coverage-79234-1779241011984-0.json +1 -0
  79. package/coverage/tmp/coverage-79235-1779241012055-0.json +1 -0
  80. package/coverage/tmp/coverage-79236-1779241012230-0.json +1 -0
  81. package/coverage/tmp/coverage-79237-1779241012300-0.json +1 -0
  82. package/coverage/tmp/coverage-79238-1779241012368-0.json +1 -0
  83. package/coverage/tmp/coverage-79239-1779241012438-0.json +1 -0
  84. package/coverage/tmp/coverage-79240-1779241012511-0.json +1 -0
  85. package/coverage/tmp/coverage-79241-1779241012582-0.json +1 -0
  86. package/coverage/tmp/coverage-79242-1779241012652-0.json +1 -0
  87. package/coverage/tmp/coverage-79243-1779241012814-0.json +1 -0
  88. package/coverage/tmp/coverage-79244-1779241012931-0.json +1 -0
  89. package/coverage/tmp/coverage-79245-1779241013007-0.json +1 -0
  90. package/coverage/tmp/coverage-79246-1779241013106-0.json +1 -0
  91. package/coverage/tmp/coverage-79247-1779241013178-0.json +1 -0
  92. package/coverage/tmp/coverage-79248-1779241013244-0.json +1 -0
  93. package/coverage/tmp/coverage-79249-1779241013409-0.json +1 -0
  94. package/coverage/tmp/coverage-79250-1779241013697-0.json +1 -0
  95. package/coverage/tmp/coverage-79251-1779241013847-0.json +1 -0
  96. package/coverage/tmp/coverage-79252-1779241014288-0.json +1 -0
  97. package/coverage/tmp/coverage-79253-1779241014378-0.json +1 -0
  98. package/coverage/tmp/coverage-79254-1779241014428-0.json +1 -0
  99. package/coverage/tmp/coverage-79255-1779241021774-0.json +1 -0
  100. package/coverage/tmp/coverage-80382-1779241021949-0.json +1 -0
  101. package/coverage/tmp/coverage-80383-1779241025019-0.json +1 -0
  102. package/coverage/tmp/coverage-80384-1779241025133-0.json +1 -0
  103. package/docs/Body.md +1 -0
  104. package/docs/Config.md +1 -0
  105. package/docs/Connection.md +153 -0
  106. package/docs/CoreConfig.md +96 -0
  107. package/docs/CustomReturnCodes.md +3 -0
  108. package/docs/HAProxy.md +62 -0
  109. package/docs/Header.md +1 -0
  110. package/docs/Logging.md +129 -0
  111. package/docs/Outbound.md +210 -0
  112. package/docs/Plugins.md +372 -0
  113. package/docs/Results.md +7 -0
  114. package/docs/Transaction.md +135 -0
  115. package/docs/Tutorial.md +183 -0
  116. package/docs/deprecated/access.md +3 -0
  117. package/docs/deprecated/backscatterer.md +9 -0
  118. package/docs/deprecated/connect.rdns_access.md +53 -0
  119. package/docs/deprecated/data.headers.md +3 -0
  120. package/docs/deprecated/data.nomsgid.md +7 -0
  121. package/docs/deprecated/data.noreceived.md +11 -0
  122. package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
  123. package/docs/deprecated/dkim_sign.md +97 -0
  124. package/docs/deprecated/dkim_verify.md +28 -0
  125. package/docs/deprecated/dnsbl.md +80 -0
  126. package/docs/deprecated/dnswl.md +73 -0
  127. package/docs/deprecated/lookup_rdns.strict.md +67 -0
  128. package/docs/deprecated/mail_from.access.md +52 -0
  129. package/docs/deprecated/mail_from.blocklist.md +18 -0
  130. package/docs/deprecated/mail_from.nobounces.md +8 -0
  131. package/docs/deprecated/rcpt_to.access.md +53 -0
  132. package/docs/deprecated/rcpt_to.blocklist.md +18 -0
  133. package/docs/deprecated/rcpt_to.routes.md +3 -0
  134. package/docs/deprecated/rdns.regexp.md +30 -0
  135. package/docs/plugins/aliases.md +3 -0
  136. package/docs/plugins/auth/auth_bridge.md +34 -0
  137. package/docs/plugins/auth/auth_ldap.md +4 -0
  138. package/docs/plugins/auth/auth_proxy.md +36 -0
  139. package/docs/plugins/auth/auth_vpopmaild.md +33 -0
  140. package/docs/plugins/auth/flat_file.md +40 -0
  141. package/docs/plugins/block_me.md +18 -0
  142. package/docs/plugins/data.signatures.md +11 -0
  143. package/docs/plugins/delay_deny.md +23 -0
  144. package/docs/plugins/max_unrecognized_commands.md +6 -0
  145. package/docs/plugins/prevent_credential_leaks.md +22 -0
  146. package/docs/plugins/process_title.md +42 -0
  147. package/docs/plugins/queue/deliver.md +3 -0
  148. package/docs/plugins/queue/discard.md +32 -0
  149. package/docs/plugins/queue/lmtp.md +24 -0
  150. package/docs/plugins/queue/qmail-queue.md +16 -0
  151. package/docs/plugins/queue/quarantine.md +87 -0
  152. package/docs/plugins/queue/smtp_bridge.md +32 -0
  153. package/docs/plugins/queue/smtp_forward.md +127 -0
  154. package/docs/plugins/queue/smtp_proxy.md +68 -0
  155. package/docs/plugins/queue/test.md +7 -0
  156. package/docs/plugins/rcpt_to.in_host_list.md +34 -0
  157. package/docs/plugins/rcpt_to.max_count.md +3 -0
  158. package/docs/plugins/record_envelope_addresses.md +20 -0
  159. package/docs/plugins/relay.md +3 -0
  160. package/docs/plugins/reseed_rng.md +16 -0
  161. package/docs/plugins/status.md +41 -0
  162. package/docs/plugins/tarpit.md +50 -0
  163. package/docs/plugins/tls.md +235 -0
  164. package/docs/plugins/toobusy.md +27 -0
  165. package/docs/plugins/xclient.md +10 -0
  166. package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
  167. package/docs/tutorials/SettingUpOutbound.md +62 -0
  168. package/eslint.config.mjs +2 -0
  169. package/haraka.js +74 -0
  170. package/haraka.sh +2 -0
  171. package/http/html/404.html +58 -0
  172. package/http/html/index.html +47 -0
  173. package/http/package.json +21 -0
  174. package/line_socket.js +24 -0
  175. package/logger.js +322 -0
  176. package/outbound/client_pool.js +59 -0
  177. package/outbound/config.js +134 -0
  178. package/outbound/hmail.js +1504 -0
  179. package/outbound/index.js +349 -0
  180. package/outbound/qfile.js +93 -0
  181. package/outbound/queue.js +399 -0
  182. package/outbound/tls.js +85 -0
  183. package/outbound/todo.js +17 -0
  184. package/package.json +99 -4
  185. package/plugins/.eslintrc.yaml +3 -0
  186. package/plugins/auth/auth_base.js +261 -0
  187. package/plugins/auth/auth_bridge.js +20 -0
  188. package/plugins/auth/auth_proxy.js +227 -0
  189. package/plugins/auth/auth_vpopmaild.js +162 -0
  190. package/plugins/auth/flat_file.js +44 -0
  191. package/plugins/block_me.js +88 -0
  192. package/plugins/data.signatures.js +30 -0
  193. package/plugins/delay_deny.js +153 -0
  194. package/plugins/prevent_credential_leaks.js +61 -0
  195. package/plugins/process_title.js +197 -0
  196. package/plugins/profile.js +11 -0
  197. package/plugins/queue/deliver.js +12 -0
  198. package/plugins/queue/discard.js +27 -0
  199. package/plugins/queue/lmtp.js +45 -0
  200. package/plugins/queue/qmail-queue.js +93 -0
  201. package/plugins/queue/quarantine.js +133 -0
  202. package/plugins/queue/smtp_bridge.js +45 -0
  203. package/plugins/queue/smtp_forward.js +371 -0
  204. package/plugins/queue/smtp_proxy.js +142 -0
  205. package/plugins/queue/test.js +15 -0
  206. package/plugins/rcpt_to.host_list_base.js +65 -0
  207. package/plugins/rcpt_to.in_host_list.js +56 -0
  208. package/plugins/record_envelope_addresses.js +17 -0
  209. package/plugins/reseed_rng.js +7 -0
  210. package/plugins/status.js +274 -0
  211. package/plugins/tarpit.js +45 -0
  212. package/plugins/tls.js +164 -0
  213. package/plugins/toobusy.js +47 -0
  214. package/plugins/xclient.js +124 -0
  215. package/plugins.js +604 -0
  216. package/queue/1772642154987_1775581346001_4_82235_TGwgfd_2_mattbook-m3.home.simerson.net +0 -0
  217. package/run_tests +11 -0
  218. package/server.js +827 -0
  219. package/smtp_client.js +504 -0
  220. package/test/.eslintrc.yaml +11 -0
  221. package/test/config/auth_flat_file.ini +5 -0
  222. package/test/config/block_me.recipient +1 -0
  223. package/test/config/block_me.senders +1 -0
  224. package/test/config/dhparams.pem +8 -0
  225. package/test/config/host_list +2 -0
  226. package/test/config/outbound_tls_cert.pem +1 -0
  227. package/test/config/outbound_tls_key.pem +1 -0
  228. package/test/config/plugins +7 -0
  229. package/test/config/smtp.ini +11 -0
  230. package/test/config/smtp_forward.ini +30 -0
  231. package/test/config/tls/example.com/_.example.com.key +28 -0
  232. package/test/config/tls/example.com/example.com.crt +25 -0
  233. package/test/config/tls/haraka.local.pem +51 -0
  234. package/test/config/tls.ini +45 -0
  235. package/test/config/tls_cert.pem +21 -0
  236. package/test/config/tls_key.pem +28 -0
  237. package/test/connection.js +817 -0
  238. package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
  239. package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
  240. package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
  241. package/test/fixtures/line_socket.js +21 -0
  242. package/test/fixtures/todo_qfile.txt +0 -0
  243. package/test/fixtures/util_hmailitem.js +156 -0
  244. package/test/installation/config/test-plugin-flat +1 -0
  245. package/test/installation/config/test-plugin.ini +10 -0
  246. package/test/installation/config/tls.ini +1 -0
  247. package/test/installation/node_modules/load_first/index.js +5 -0
  248. package/test/installation/node_modules/load_first/package.json +11 -0
  249. package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
  250. package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
  251. package/test/installation/node_modules/test-plugin/package.json +5 -0
  252. package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
  253. package/test/installation/plugins/base_plugin.js +3 -0
  254. package/test/installation/plugins/folder_plugin/index.js +3 -0
  255. package/test/installation/plugins/folder_plugin/package.json +11 -0
  256. package/test/installation/plugins/inherits.js +7 -0
  257. package/test/installation/plugins/load_first.js +3 -0
  258. package/test/installation/plugins/plugin.js +1 -0
  259. package/test/installation/plugins/tls.js +3 -0
  260. package/test/logger.js +217 -0
  261. package/test/loud/config/dhparams.pem +0 -0
  262. package/test/loud/config/tls/goobered.pem +45 -0
  263. package/test/loud/config/tls.ini +43 -0
  264. package/test/mail_specimen/base64-root-part.txt +23 -0
  265. package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
  266. package/test/outbound/bounce_net_errors.js +133 -0
  267. package/test/outbound/bounce_rfc3464.js +226 -0
  268. package/test/outbound/hmail.js +210 -0
  269. package/test/outbound/index.js +385 -0
  270. package/test/outbound/qfile.js +124 -0
  271. package/test/outbound/queue.js +325 -0
  272. package/test/plugins/auth/auth_base.js +620 -0
  273. package/test/plugins/auth/auth_bridge.js +80 -0
  274. package/test/plugins/auth/auth_vpopmaild.js +81 -0
  275. package/test/plugins/auth/flat_file.js +123 -0
  276. package/test/plugins/block_me.js +141 -0
  277. package/test/plugins/data.signatures.js +111 -0
  278. package/test/plugins/delay_deny.js +262 -0
  279. package/test/plugins/prevent_credential_leaks.js +174 -0
  280. package/test/plugins/process_title.js +141 -0
  281. package/test/plugins/queue/deliver.js +98 -0
  282. package/test/plugins/queue/discard.js +78 -0
  283. package/test/plugins/queue/lmtp.js +137 -0
  284. package/test/plugins/queue/qmail-queue.js +98 -0
  285. package/test/plugins/queue/quarantine.js +80 -0
  286. package/test/plugins/queue/smtp_bridge.js +152 -0
  287. package/test/plugins/queue/smtp_forward.js +1023 -0
  288. package/test/plugins/queue/smtp_proxy.js +138 -0
  289. package/test/plugins/rcpt_to.host_list_base.js +102 -0
  290. package/test/plugins/rcpt_to.in_host_list.js +186 -0
  291. package/test/plugins/record_envelope_addresses.js +66 -0
  292. package/test/plugins/reseed_rng.js +34 -0
  293. package/test/plugins/status.js +207 -0
  294. package/test/plugins/tarpit.js +90 -0
  295. package/test/plugins/tls.js +86 -0
  296. package/test/plugins/toobusy.js +21 -0
  297. package/test/plugins/xclient.js +119 -0
  298. package/test/plugins.js +230 -0
  299. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  300. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  301. package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  302. package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  303. package/test/queue/zero-length +0 -0
  304. package/test/server.js +1012 -0
  305. package/test/smtp_client.js +1303 -0
  306. package/test/tls_socket.js +321 -0
  307. package/test/transaction.js +554 -0
  308. package/tls_socket.js +771 -0
  309. package/transaction.js +267 -0
@@ -0,0 +1,183 @@
1
+ # Writing Haraka Plugins
2
+
3
+ Part of the joy of using Haraka as your main mail server is having a strong plugin system: you control every aspect of how mail is processed, accepted, and delivered.
4
+
5
+ This tutorial walks through a small plugin that supports *disposable addresses*: an email like `user-20271231@example.com` is accepted up until 31 December 2027, after which delivery is rejected. Mail that is still within the validity window is rewritten back to `user@example.com` before it is forwarded on.
6
+
7
+ ## What You'll Need
8
+
9
+ - Node.js (an active LTS release) and npm
10
+ - Haraka
11
+ - A text editor
12
+ - [swaks][swaks] for sending test mail
13
+
14
+ ## Getting Started
15
+
16
+ Install Haraka and create a project:
17
+
18
+ ```sh
19
+ sudo npm install -g Haraka
20
+ haraka -i /path/to/new_project
21
+ ```
22
+
23
+ Use a directory that does not yet exist. Now scaffold a plugin:
24
+
25
+ ```sh
26
+ haraka -c /path/to/new_project -p rcpt_to.disposable
27
+ ```
28
+
29
+ `haraka -p` reports the files it created:
30
+
31
+ ```
32
+ Plugin rcpt_to.disposable created
33
+ Now edit javascript in: /path/to/new_project/plugins/rcpt_to.disposable.js
34
+ Add the plugin to config: /path/to/new_project/config/plugins
35
+ And edit documentation in: /path/to/new_project/docs/plugins/rcpt_to.disposable.md
36
+ ```
37
+
38
+ Edit `config/plugins` so the only enabled lines are:
39
+
40
+ ```
41
+ rcpt_to.disposable
42
+ rcpt_to.in_host_list
43
+ queue/test
44
+ ```
45
+
46
+ The ordering matters — the disposable plugin must run *before* `rcpt_to.in_host_list`, which accepts mail for domains listed in `config/host_list`. `queue/test` writes accepted mail to a `.eml` file in `os.tmpdir()` so you can confirm delivery.
47
+
48
+ Open `plugins/rcpt_to.disposable.js` and start with:
49
+
50
+ ```js
51
+ exports.hook_rcpt = (next, connection, params) => {
52
+ const rcpt = params[0]
53
+ connection.loginfo(`got recipient: ${rcpt}`)
54
+ next()
55
+ }
56
+ ```
57
+
58
+ Verify it works. In one terminal:
59
+
60
+ ```sh
61
+ echo LOGDEBUG > config/loglevel
62
+ echo myserver.com >> config/host_list
63
+ sudo haraka -c /path/to/new_project
64
+ ```
65
+
66
+ In another:
67
+
68
+ ```sh
69
+ swaks -h example.com -t booya@myserver.com -f sender@example.com -s localhost -p 25
70
+ ```
71
+
72
+ You should see something like this in the Haraka log:
73
+
74
+ ```
75
+ [INFO] [<uuid>] [rcpt_to.disposable] got recipient: <booya@myserver.com>
76
+ ```
77
+
78
+ …and a `.eml` file in your system temp directory containing the message.
79
+
80
+ ## Parsing Out the Date
81
+
82
+ Detect addresses of the form `user-YYYYMMDD` and parse the date:
83
+
84
+ ```js
85
+ exports.hook_rcpt = (next, connection, params) => {
86
+ const rcpt = params[0]
87
+ connection.loginfo(`got recipient: ${rcpt}`)
88
+
89
+ const match = /^(.*)-(\d{4})(\d{2})(\d{2})$/.exec(rcpt.user)
90
+ if (!match) return next()
91
+
92
+ // Date constructor uses zero-indexed months (Dec === 11)
93
+ const expiry = new Date(match[2], match[3] - 1, match[4])
94
+ connection.loginfo(`expires on: ${expiry.toISOString()}`)
95
+
96
+ next()
97
+ }
98
+ ```
99
+
100
+ Restart Haraka and send:
101
+
102
+ ```sh
103
+ swaks -h example.com -t booya-20271231@myserver.com \
104
+ -f sender@example.com -s localhost -p 25
105
+ ```
106
+
107
+ Logs:
108
+
109
+ ```
110
+ [INFO] [rcpt_to.disposable] got recipient: <booya-20271231@myserver.com>
111
+ [INFO] [rcpt_to.disposable] expires on: 2027-12-31T00:00:00.000Z
112
+ ```
113
+
114
+ ## Rejecting Expired Addresses
115
+
116
+ Compare the parsed date to today and reject if it has already passed:
117
+
118
+ ```js
119
+ exports.hook_rcpt = (next, connection, params) => {
120
+ const rcpt = params[0]
121
+
122
+ const match = /^(.*)-(\d{4})(\d{2})(\d{2})$/.exec(rcpt.user)
123
+ if (!match) return next()
124
+
125
+ const expiry = new Date(match[2], match[3] - 1, match[4])
126
+ if (expiry < new Date()) {
127
+ return next(DENY, 'Expired email address')
128
+ }
129
+
130
+ next()
131
+ }
132
+ ```
133
+
134
+ Send mail to an expired address:
135
+
136
+ ```sh
137
+ swaks -h example.com -t booya-20200101@myserver.com \
138
+ -f sender@example.com -s localhost -p 25
139
+ ```
140
+
141
+ The remote end sees:
142
+
143
+ ```
144
+ <** 550 Expired email address
145
+ ```
146
+
147
+ ## Rewriting Live Addresses
148
+
149
+ When the address is still valid, strip the date tag so the downstream mail store receives plain `user@domain`:
150
+
151
+ ```js
152
+ exports.hook_rcpt = (next, connection, params) => {
153
+ const rcpt = params[0]
154
+
155
+ const match = /^(.*)-(\d{4})(\d{2})(\d{2})$/.exec(rcpt.user)
156
+ if (!match) return next()
157
+
158
+ const expiry = new Date(match[2], match[3] - 1, match[4])
159
+ if (expiry < new Date()) {
160
+ return next(DENY, 'Expired email address')
161
+ }
162
+
163
+ rcpt.user = match[1]
164
+ connection.loginfo(`rewrote recipient to: ${rcpt}`)
165
+ next()
166
+ }
167
+ ```
168
+
169
+ Send to a live tagged address and watch the log:
170
+
171
+ ```
172
+ [INFO] [rcpt_to.disposable] rewrote recipient to: <booya@myserver.com>
173
+ ```
174
+
175
+ ## Further Reading
176
+
177
+ The Haraka API offers much more — body and header access, ESMTP extension hooks, the outbound delivery hooks, structured results, attachments, and so on. Two good starting points:
178
+
179
+ - The [Plugins guide](Plugins.md) and the rest of the `docs/` directory.
180
+ - The [Plugin registry](https://github.com/haraka/Haraka/blob/master/PLUGINS.md) for an inventory of real-world plugins.
181
+ - The plugins shipped in the [`plugins/`](../plugins/) directory. Even the most elaborate are under 200 lines; many are under 20.
182
+
183
+ [swaks]: https://www.jetmore.org/john/code/swaks/
@@ -0,0 +1,3 @@
1
+ # access - ACLs
2
+
3
+ Repackaged as [haraka-plugin-access](https://github.com/haraka/haraka-plugin-access).
@@ -0,0 +1,9 @@
1
+ # backscatterer
2
+
3
+ This is a very basic pluign that checks the connecting IP against
4
+ ips.backscatterer.org when the envelope-from is null or postmaster@
5
+ as per the instructions at http://www.backscatterer.org/?target=usage
6
+
7
+ This plugin is used to reject misdirected bounces and autoresponders
8
+ and sender callouts from abusive systems which can happen when a
9
+ local domain is spoofed and used as the envelope-from in a spam run.
@@ -0,0 +1,53 @@
1
+ ## DEPRECATION NOTICE
2
+
3
+ See [haraka-plugin-access](https://github.com/haraka/haraka-plugin-access)
4
+ for upgrade instructions.
5
+
6
+ # connect.rdns_access
7
+
8
+ This plugin will evaluate the remote IP address and the remote rDNS hostname
9
+ against a set of white and black lists. The lists are applied in the following
10
+ way:
11
+
12
+ connect.rdns_access.whitelist (pass)
13
+ connect.rdns_access.whitelist_regex (pass)
14
+ connect.rdns_access.blacklist (block)
15
+ connect.rdns_access.blacklist_regex (block)
16
+
17
+ ## Configuration connect.rdns_access.ini
18
+
19
+ General configuration file for this plugin.
20
+
21
+ - connect.rdns_access.general.deny_msg
22
+
23
+ Text to send the user on reject (text).
24
+
25
+ ## Configuration connect.rdns_access.whitelist
26
+
27
+ The whitelist is mostly to counter blacklist entries that match more than
28
+ what one would want. This file should be used for a specific IP address
29
+ or rDNS hostnames, one per line, that should bypass blacklist checks.
30
+ NOTE: We heavily suggest tailoring blacklist entries to be as accurate as
31
+ possible and never using whitelists. Nevertheless, if you need whitelists,
32
+ here they are.
33
+
34
+ ## Configuration connect.rdns_access.whitelist_regex
35
+
36
+ Does the same thing as the whitelist file, but each line is a regex.
37
+ Each line is also anchored for you, meaning '^' + regex + '$' is added for
38
+ you. If you need to get around this restriction, you may use a '.\*' at
39
+ either the start or the end of your regex. This should help prevent people
40
+ from writing overly permissive rules on accident.
41
+
42
+ ## Configuration connect.rdns_access.blacklist
43
+
44
+ This file should be used for a specific IP address or rDNS hostnames, one
45
+ per line, that should fail on connect.
46
+
47
+ ## Configuration connect.rdns_access.blacklist_regex
48
+
49
+ Does the same thing as the blacklist file, but each line is a regex.
50
+ Each line is also anchored for you, meaning '^' + regex + '$' is added for
51
+ you. If you need to get around this restriction, you may use a '.\*' at
52
+ either the start or the end of your regex. This should help prevent people
53
+ from writing overly permissive rules on accident.
@@ -0,0 +1,3 @@
1
+ # data.headers
2
+
3
+ Deprecated by [haraka-plugin-headers](https://github.com/haraka/haraka-plugin-headers/)
@@ -0,0 +1,7 @@
1
+ # data.nomsgid
2
+
3
+ NOTICE: this plugin is deprecated. Use data.headers instead.
4
+
5
+ Quite simply enabling this plugin blocks all mails lacking a Message-Id
6
+ header. This is an aggressive anti-spam measure, but since most mail systems
7
+ will add a Message-Id header, it tends to block a good chunk of abusive mail.
@@ -0,0 +1,11 @@
1
+ # data.noreceived
2
+
3
+ NOTICE: this plugin is deprecated. Use data.headers instead.
4
+
5
+ This plugin very simply blocks any mail arriving at your system that has no
6
+ `Received` headers.
7
+
8
+ This is an aggressive anti-spam measure, but works because all real mail
9
+ relays will add a `Received` header according to the RFCs. It may false
10
+ positive on some bulk mail that uses a custom tool to send, but this appears
11
+ to be fairly rare.
@@ -0,0 +1,11 @@
1
+ # data.rfc5322_header_checks
2
+
3
+ NOTICE: this plugin is deprecated. Use data.headers instead.
4
+
5
+ This plugin enforces RFC 5322 Section 3.6 which states that:
6
+
7
+ All messages MUST have a 'Date' and 'From' header and a message may not contain
8
+ more than one 'Date', 'From', 'Sender', 'Reply-To', 'To', 'Cc', 'Bcc',
9
+ 'Message-Id', 'In-Reply-To', 'References' or 'Subject' header.
10
+
11
+ Any message that does not meet these requirements will be rejected.
@@ -0,0 +1,97 @@
1
+ # dkim_sign
2
+
3
+ This plugin implements the [DKIM Core specification](dkimcore.org).
4
+
5
+ This plugin only _signs_ outbound messages. It does not validate DKIM signatures.
6
+
7
+ ## Getting Started
8
+
9
+ Generate a DKIM selector and keys for your domain:
10
+
11
+ ```sh
12
+ cd /path/to/haraka/config/dkim
13
+ ./dkim_key_gen.sh example.org
14
+ ```
15
+
16
+ Within the config/dkim/${domain} directory will be 4 files:
17
+
18
+ ```sh
19
+ ls config/dkim/example.org/
20
+ dns private public selector
21
+ ```
22
+
23
+ The selector file contains the DNS label where the DKIM public key is published. The `private` and `public` files contain the DKIM keys.
24
+
25
+ The `dns` file contains a formatted record of the public key suitable for copy/pasting into your domains zone file. It also has suggestions for DKIM, SPF, and DMARC policy records.
26
+
27
+ The DKIM DNS record will look like this:
28
+
29
+ may2013._domainkey TXT "v=DKIM1;p=[public key stripped of whitespace];"
30
+
31
+ The values in the address have the following meaning:
32
+
33
+ hash: h=[ sha1 | sha256 ]
34
+ test; t=[ s | s:y ]
35
+ granularity: g=[ ]
36
+ notes: n=[ ]
37
+ services: s=[email]
38
+ keytypes: [ rsa ]
39
+
40
+ ## Key size
41
+
42
+ The default key size created by `dkim_key_gen.sh` is 2048. That is considered secure as of mid-2014 but after 2020, you should be using 4096.
43
+
44
+ # What to sign
45
+
46
+ The DKIM signing key for messages from example.org _should_ be signed with
47
+ a DKIM key for example.org. Failing to do so will result in messages not
48
+ having an _aligned_ DKIM signature. For DMARC enabled domains, this will
49
+ likely result in deliverability problems.
50
+
51
+ For correct alignment, Haraka signs each message with that domains DKIM key.
52
+ For an alternative, see the legacy Single Domain Configuration below.
53
+
54
+ # Configuration
55
+
56
+ This plugin is configured in `dkim_sign.ini`.
57
+
58
+ - disabled = [ 1 | true | yes ] (OPTIONAL)
59
+
60
+ Set this to disable DKIM signing
61
+
62
+ - headers_to_sign = list, of; headers (REQUIRED)
63
+
64
+ Set this to the list of headers that should be signed, separated by commas, colons or semi-colons. Signing prevents tampering with the specified headers.
65
+ The 'From' header is required by the RFC and will be added if missing.
66
+
67
+ ## Single Domain Configuration
68
+
69
+ To sign all messages with a single DKIM key, you must set the selector and domain in dkim_sign.ini. You must also save your DKIM private key in the file `dkim.private.key` in the Haraka config directory.
70
+
71
+ - selector = name
72
+
73
+ Set this to the selector name published in DNS under the
74
+ \_domainkey sub-domain of the domain referenced below.
75
+
76
+ - domain = name
77
+
78
+ Set this to the domain name that will be used to sign messages
79
+ which don't match a per-domain DKIM key. The DNS TXT entry for:
80
+
81
+ <selector>._domainkey.<domain>
82
+
83
+ Test that your DKIM key is published properly with a DNS request like this:
84
+
85
+ ```sh
86
+ drill TXT $SELECTOR._domainkey.$DOMAIN
87
+ dig TXT $SELECTOR._domainkey.$DOMAIN +short
88
+ ```
89
+
90
+ ### Example DNS query
91
+
92
+ ```sh
93
+ export SELECTOR=mar2013
94
+ export DOMAIN=simerson.net
95
+ $ dig TXT $SELECTOR._domainkey.$DOMAIN +short
96
+ "v=DKIM1;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoyUzGOTSOmakY8BcxXgi0mN/nFegLBPs7aaGQUtjHfa8yUrt9T2j6GSXgdjLuG3R43WjePQv3RHzc+bwwOkdw0XDOXiztn5mhrlaflbVr5PMSTrv64/cpFQKLtgQx8Vgqp7Dh3jw13rLomRTqJFgMrMHdhIibZEa69gtuAfDqoeXo6QDSGk5JuBAeRHEH27FriHulg5ob" "4F4lmh7fMFVsDGkQEF6jaIVYqvRjDyyQed3R3aTJX3fpb3QrtRqvfn/LAf+3kzW58AjsERpsNCSTD2RquxbnyoR/1wdGKb8cUlD/EXvqtvpVnOzHeSeMEqex3kQI8HOGsEehWZlKd+GqwIDAQAB"
97
+ ```
@@ -0,0 +1,28 @@
1
+ # dkim_verify
2
+
3
+ This plugin will verify DKIM signatures as defined by RFC 6376 and add
4
+ an Authentication-Results header as appropriate.
5
+
6
+ ## Configuration
7
+
8
+ - allowed_time_skew
9
+
10
+ How far can we stretch on time matching, in secs. Useful when clock is skewed.
11
+
12
+ - sigerror_log_level
13
+
14
+ ## Testing
15
+
16
+ This plugin also provides a command-line test tool that can be used to
17
+ debug DKIM issues or to check results.
18
+
19
+ ```
20
+ # dkimverify < message
21
+ identity="@gmail.com" domain="gmail.com" result=pass
22
+ ```
23
+
24
+ You can add `--debug` to the option arguments to see a full trace of the processing.
25
+
26
+ ## Notes
27
+
28
+ This plugin and underlying library does not currently support DKIM body length limits (l=).
@@ -0,0 +1,80 @@
1
+ # dnsbl
2
+
3
+ This plugin looks up the connecting IP address in an IP blocklist. Mails
4
+ found to be in the blocklist are rejected.
5
+
6
+ ## Configuration
7
+
8
+ This plugins uses the following files:
9
+
10
+ dnsbl.zones - Contains a list of zones to query, one per line.
11
+
12
+ dnsbl.ini - INI format with options described below:
13
+
14
+ - zones
15
+
16
+ A comma or semi-colon list of zones to query. It will be merged with
17
+ any lists in dnsbl.zones.
18
+
19
+ - periodic_checks
20
+
21
+ If enabled, this will check all the zones every n minutes.
22
+ The minimum value that will be accepted here is 5. Any value less
23
+ than 5 will cause the checks to be run at start-up only.
24
+
25
+ The checks confirm that the list is responding and that it is not
26
+ listing the world. If any errors are detected, then the zone is
27
+ disabled and will be re-checked on the next test. If a zone
28
+ subsequently starts working correctly then it will be re-enabled.
29
+
30
+ - enable_stats
31
+
32
+ To use this feature you must have installed the 'redis' module and
33
+ have a redis server running.
34
+
35
+ When enabled, this will record several list statistics to redis.
36
+
37
+ It will track the total number of queries (TOTAL) and the average
38
+ response time (AVG_RT) and the return type (e.g. LISTED or ERROR)
39
+ to a redis hash where the key is 'dns-list-stat:zone' and the hash
40
+ field is the response type.
41
+
42
+ It will also track the positive response overlap between the lists
43
+ in another redis hash where the key is 'dns-list-overlap:zone' and
44
+ the hash field is the other list names.
45
+
46
+ Example:
47
+ <pre><code>redis 127.0.0.1:6379> hgetall dns-list-stat:zen.spamhaus.org
48
+ 1) "TOTAL"
49
+ 2) "23"
50
+ 3) "ENOTFOUND"
51
+ 4) "11"
52
+ 5) "LISTED"
53
+ 6) "12"
54
+ 7) "AVG_RT"
55
+ 8) "45.5"
56
+ redis 127.0.0.1:6379> hgetall dns-list-overlap:zen.spamhaus.org
57
+ 1) "b.barracudacentral.org"
58
+ 2) "1"
59
+ 3) "bl.spamcop.net"
60
+ 4) "1"
61
+ 5) "TOTAL"
62
+ 6) "1"
63
+ </code></pre>
64
+
65
+ - stats_redis_host
66
+
67
+ In the form of `host:port` this option allows you to specify a different
68
+ host on which redis runs.
69
+
70
+ - reject (default: true)
71
+
72
+ Reject connections from IPs that are blacklisted. Setting this to false
73
+ makes dnsbl informational. reject=false is best used in conjunction with
74
+ plugins like [karma](/manual/plugins/karma.html) that employ a scoring
75
+ engine to make choices about message delivery.
76
+
77
+ - search: (default: first)
78
+
79
+ first: consider first DNSBL response conclusive. End processing.
80
+ all: process all DNSBL results
@@ -0,0 +1,73 @@
1
+ # dnswl
2
+
3
+ This plugin looks up the connecting IP address in an IP whitelist.
4
+ If the host is listed, then the plugin will return OK for all hooks
5
+ up to hook_data.
6
+
7
+ IMPORTANT! The order of plugins in config/plugins is important when
8
+ this plugin is used. It should be listed _before_ any plugins that
9
+ you wish to skip, but after any plugins that accept recipients.
10
+
11
+ ## Configuration
12
+
13
+ This plugins uses the following files:
14
+
15
+ dnswl.zones - Contains a list of zones to query, one per line.
16
+
17
+ dnswl.ini - INI format with options described below:
18
+
19
+ - zones
20
+
21
+ A comma or semi-colon list of zones to query. It will be merged with
22
+ any lists in dnswl.zones.
23
+
24
+ - periodic_checks
25
+
26
+ If enabled, this will check all the zones every n minutes.
27
+ The minimum value that will be accepted here is 5. Any value less
28
+ than 5 will cause the checks to be run at start-up only.
29
+
30
+ The checks confirm that the list is responding and that it is not
31
+ listing the world. If any errors are detected, then the zone is
32
+ disabled and will be re-checked on the next test. If a zone
33
+ subsequently starts working correctly then it will be re-enabled.
34
+
35
+ - enable_stats
36
+
37
+ To use this feature you must have installed the 'redis' module and
38
+ have a redis server running.
39
+
40
+ When enabled, this will record several list statistics to redis.
41
+
42
+ It will track the total number of queries (TOTAL) and the average
43
+ response time (AVG_RT) and the return type (e.g. LISTED or ERROR)
44
+ to a redis hash where the key is 'dns-list-stat:zone' and the hash
45
+ field is the response type.
46
+
47
+ It will also track the positive response overlap between the lists
48
+ in another redis hash where the key is 'dns-list-overlap:zone' and
49
+ the hash field is the other list names.
50
+
51
+ Example:
52
+ <pre><code>redis 127.0.0.1:6379> hgetall dns-list-stat:zen.spamhaus.org
53
+ 1) "TOTAL"
54
+ 2) "23"
55
+ 3) "ENOTFOUND"
56
+ 4) "11"
57
+ 5) "LISTED"
58
+ 6) "12"
59
+ 7) "AVG_RT"
60
+ 8) "45.5"
61
+ redis 127.0.0.1:6379> hgetall dns-list-overlap:zen.spamhaus.org
62
+ 1) "b.barracudacentral.org"
63
+ 2) "1"
64
+ 3) "bl.spamcop.net"
65
+ 4) "1"
66
+ 5) "TOTAL"
67
+ 6) "1"
68
+ </code></pre>
69
+
70
+ - stats_redis_host
71
+
72
+ In the form of `host:port` this option allows you to specify a different
73
+ host on which redis runs.
@@ -0,0 +1,67 @@
1
+ # lookup_rdns.strict
2
+
3
+ This plugin checks the reverse-DNS and compares the resulting addresses
4
+ against forward DNS for a match. If there is no match it sends a
5
+ DENYDISCONNECT, otherwise if it matches it sends an OK. DENYDISCONNECT
6
+ messages are configurable.
7
+
8
+ ## Configuration lookup_rdns.strict.ini
9
+
10
+ This is the general configuration file for the plugin. In it you can find
11
+ ways to customize user messages, specify timeouts, and some whitelist
12
+ parsing options.
13
+
14
+ - lookup_rdns.strict.general.nomatch
15
+
16
+ Text to send the user if there is no reverse to forward match (text).
17
+
18
+ - lookup_rdns.strict.general.timeout
19
+
20
+ How long we should give this plugin before we time it out (seconds).
21
+
22
+ - lookup_rdns.strict.general.timeout_msg
23
+
24
+ Text to send when plugin reaches timeout (text).
25
+
26
+ - lookup_rdns.strict.forward.nxdomain
27
+
28
+ Text to send the user if there is no forward match (text).
29
+
30
+ - lookup_rdns.strict.forward.dnserror
31
+
32
+ Text to send the user if there is some other error with the forward
33
+ lookup (text).
34
+
35
+ - lookup_rdns.strict.reverse.nxdomain
36
+
37
+ Text to send the user if there is no reverse match (text).
38
+
39
+ - lookup_rdns.strict.reverse.dnserror
40
+
41
+ Text to send the user if there is some other error with the reverse
42
+ lookup (text).
43
+
44
+ ## Configuration lookup_rdns.strict.timeout
45
+
46
+ This is how we specify to Haraka that our plugin should have a certain timeout.
47
+ If you specify 0 here, then the plugin will never timeout while the connection
48
+ is active. This is also required for this plugin, which needs to handle its
49
+ own timeouts. To actually specify the timeout for this plugin, please see
50
+ the general config in lookup_rdns.strict.ini.
51
+
52
+ ## Configuration lookup_rdns.strict.whitelist
53
+
54
+ No matter how much you believe in checking that DNS and rDNS match, it is not
55
+ required by RFC, and there will always be some legitimate mail server that
56
+ has great trouble getting their DNS in order. For this reason we are
57
+ providing a whitelist.
58
+
59
+ This file will match exactly what you put on each line.
60
+
61
+ ## Configuration lookup_rdns.strict.whitelist_regex
62
+
63
+ Does the same thing as the whitelist file, but each line is a regex.
64
+ Each line is also anchored for you, meaning '^' + regex + '$' is added for
65
+ you. If you need to get around this restriction, you may use a '.\*' at
66
+ either the start or the end of your regex. This should help prevent people
67
+ from writing overly permissive rules on accident.
@@ -0,0 +1,52 @@
1
+ ## DEPRECATION NOTICE
2
+
3
+ See [haraka-plugin-access](https://github.com/haraka/haraka-plugin-access)
4
+ for upgrade instructions.
5
+
6
+ # mail_from.access
7
+
8
+ This plugin will evaluate the address against a set of white and black lists.
9
+ The lists are applied in the following way:
10
+
11
+ mail_from.access.whitelist (pass)
12
+ mail_from.access.whitelist_regex (pass)
13
+ mail_from.access.blacklist (block)
14
+ mail_from.access.blacklist_regex (block)
15
+
16
+ ## Configuration mail_from.access.ini
17
+
18
+ General configuration file for this plugin.
19
+
20
+ - mail_from.access.general.deny_msg
21
+
22
+ Text to send the user on reject (text).
23
+
24
+ ## Configuration mail_from.access.whitelist
25
+
26
+ The whitelist is mostly to counter blacklist entries that match more than
27
+ what one would want. This file should be used for a specific address,
28
+ one per line, that should bypass blacklist checks.
29
+ NOTE: We heavily suggest tailoring blacklist entries to be as accurate as
30
+ possible and never using whitelists. Nevertheless, if you need whitelists,
31
+ here they are.
32
+
33
+ ## Configuration mail_from.access.whitelist_regex
34
+
35
+ Does the same thing as the whitelist file, but each line is a regex.
36
+ Each line is also anchored for you, meaning '^' + regex + '$' is added for
37
+ you. If you need to get around this restriction, you may use a '.\*' at
38
+ either the start or the end of your regex. This should help prevent people
39
+ from writing overly permissive rules on accident.
40
+
41
+ ## Configuration mail_from.access.blacklist
42
+
43
+ This file should be used for a specific address, one per line, that should
44
+ fail on connect.
45
+
46
+ ## Configuration mail_from.access.blacklist_regex
47
+
48
+ Does the same thing as the blacklist file, but each line is a regex.
49
+ Each line is also anchored for you, meaning '^' + regex + '$' is added for
50
+ you. If you need to get around this restriction, you may use a '.\*' at
51
+ either the start or the end of your regex. This should help prevent people
52
+ from writing overly permissive rules on accident.