Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZMS-171: Add support for an outbound MTA relay #787

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions lib/api/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -2634,6 +2634,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let message = result.value.message;

let messageData;
let userData;
try {
messageData = await db.database.collection('messages').findOne(
{
Expand All @@ -2653,6 +2654,18 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
}
}
);

userData = await db.database.collection('users').findOne(
{
_id: user
},
{
projection: {
_id: true,
mtaRelay: true
}
}
);
} catch (err) {
res.status(500);
return res.json({
Expand Down Expand Up @@ -2705,7 +2718,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
sender: messageData.meta.from,
recipient: messageData.meta.to,
targets: forwardTargets,
stream: response.value
stream: response.value,
userData
};

let queueId;
Expand Down Expand Up @@ -3981,7 +3995,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
to: envelope.to,
sendTime,
origin: options.origin || options.ip,
runPlugins: true
runPlugins: true,
mtaRelay: userData.mtaRelay || false
},
(err, ...args) => {
if (err || !args[0]) {
Expand Down
6 changes: 4 additions & 2 deletions lib/api/submit.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ module.exports = (db, server, messageHandler, userHandler, settingsHandler) => {
pubKey: true,
disabled: true,
suspended: true,
fromWhitelist: true
fromWhitelist: true,
mtaRelay: true
}
},
(err, userData) => {
Expand Down Expand Up @@ -472,7 +473,8 @@ module.exports = (db, server, messageHandler, userHandler, settingsHandler) => {
to: compiledEnvelope.to,
sendTime,
origin: options.ip,
runPlugins: true
runPlugins: true,
mtaRelay: userData.mtaRelay || false
},
(err, ...args) => {
if (err || !args[0]) {
Expand Down
39 changes: 39 additions & 0 deletions lib/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
'An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25") or an URL where mail contents are POSTed to'
),

mtaRelay: Joi.string()
.uri({
scheme: [/smtps?/],
allowRelative: false,
relativeOnly: false
})
.description('An address of an SMTP MTA relay. The value should be a relay url. If specified uses the this relay as the outbound MTA.'),

spamLevel: Joi.number()
.min(0)
.max(100)
Expand Down Expand Up @@ -500,6 +508,7 @@ module.exports = (db, server, userHandler, settingsHandler) => {
let values = permission.filter(result.value);

let targets = values.targets;
let mtaRelay = values.mtaRelay;

if (targets) {
for (let i = 0, len = targets.length; i < len; i++) {
Expand Down Expand Up @@ -535,6 +544,15 @@ module.exports = (db, server, userHandler, settingsHandler) => {
values.targets = targets;
}

if (mtaRelay && /^smtps?:/i.test(mtaRelay)) {
mtaRelay = {
id: new ObjectId(),
type: 'relay',
value: mtaRelay // current mtaRelay string value
};
values.mtaRelay = mtaRelay;
}

if ('pubKey' in req.params && !values.pubKey) {
values.pubKey = '';
}
Expand Down Expand Up @@ -796,6 +814,7 @@ module.exports = (db, server, userHandler, settingsHandler) => {
.required()
.description('Custom internal metadata object set for this user. Not available for user-role tokens'),
targets: Joi.array().items(Joi.string()).required().description('List of forwarding targets'),
mtaRelay: Joi.string().required().description('MTA Relay url'),
spamLevel: Joi.number()
.required()
.description('Relative scale for detecting spam. 0 means that everything is spam, 100 means that nothing is spam'),
Expand Down Expand Up @@ -1073,6 +1092,8 @@ module.exports = (db, server, userHandler, settingsHandler) => {
.map(target => target.value)
.filter(target => target),

mtaRelay: userData.mtaRelay?.value || false,

limits: {
quota: {
allowed: Number(userData.quota) || settings['const:max:storage'],
Expand Down Expand Up @@ -1194,6 +1215,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
'An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25") or an URL where mail contents are POSTed to'
),

mtaRelay: Joi.string()
.uri({
scheme: [/smtps?/],
allowRelative: false,
relativeOnly: false
})
.description('An address of an SMTP MTA relay. The value should be a relay url. If specified uses the this relay as the outbound MTA.'),

spamLevel: Joi.number()
.min(0)
.max(100)
Expand Down Expand Up @@ -1326,6 +1355,7 @@ module.exports = (db, server, userHandler, settingsHandler) => {

let targets = values.targets;
let existingTargets;
let mtaRelay = values.mtaRelay;

if (targets) {
for (let i = 0, len = targets.length; i < len; i++) {
Expand Down Expand Up @@ -1382,6 +1412,15 @@ module.exports = (db, server, userHandler, settingsHandler) => {
}
}

if (mtaRelay && /^smtps?:/i.test(mtaRelay)) {
mtaRelay = {
id: new ObjectId(),
type: 'relay',
value: mtaRelay // current mtaRelay string value
};
values.mtaRelay = mtaRelay;
}

if (!values.name && 'name' in req.params) {
values.name = '';
}
Expand Down
3 changes: 2 additions & 1 deletion lib/autoreply.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ async function autoreply(options, autoreplyData) {
reason: 'autoreply',
from: '',
to: options.sender,
interface: 'autoreplies'
interface: 'autoreplies',
mtaRelay: options.userData?.mtaRelay || false
},
(err, ...args) => {
if (err || !args[0]) {
Expand Down
3 changes: 2 additions & 1 deletion lib/filter-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class FilterHandler {
encryptForwarded: true,
pubKey: true,
spamLevel: true,
tagsview: true
tagsview: true,
mtaRelay: true
};

if (collection === 'users') {
Expand Down
4 changes: 3 additions & 1 deletion lib/forward.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ module.exports = (options, callback) => {

targets: options.targets,

interface: 'forwarder'
interface: 'forwarder',

mtaRelay: options.userData?.mtaRelay || false
};

let message = options.maildrop.push(mail, (err, ...args) => {
Expand Down
48 changes: 40 additions & 8 deletions lib/maildropper.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,42 @@ class Maildropper {

let deliveries = [];

let mxData = false;

if (options.mtaRelay) {
// user has MTA Relay set, use it to send outbound email
let relayData = options.mtaRelay.value;
if (typeof relayData === 'string') {
relayData = tools.getRelayData(relayData);
}

mxData = {
mx: relayData.mx,
mxPort: relayData.mxPort,
mxAuth: relayData.mxAuth,
mxSecure: relayData.mxSecure,
skipSRS: true,
skipSTS: true
};
}

if (options.targets) {
options.targets.forEach(target => {
switch (target.type) {
case 'mail':
deliveries.push({
to: target.value,
forwardedFor: target.recipient
});
break;
{
let delivery = {
to: target.value,
forwardedFor: target.recipient
};

if (mxData) {
delivery = { ...delivery, ...mxData };
}

deliveries.push(delivery);
}
break;
case 'relay':
{
let recipients = new Set([].concat(options.to || []).concat(target.recipient || []));
Expand Down Expand Up @@ -196,9 +222,15 @@ class Maildropper {
}

if (!deliveries.length) {
deliveries = envelope.to.map(to => ({
to
}));
deliveries = envelope.to.map(to => {
let delivery = { to };

if (mxData) {
delivery = { ...delivery, ...mxData };
}

return delivery;
});
}

if (!deliveries.length) {
Expand Down
2 changes: 1 addition & 1 deletion setup/09_install_zone_mta.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ secret=\"$ZONEMTA_SECRET\"
algo=\"md5\"" > /etc/zone-mta/plugins/loop-breaker.toml

echo "[wildduck]
enabled=[\"receiver\", \"sender\"]
enabled=[\"receiver\", \"sender\", \"main\"]

# which interfaces this plugin applies to
interfaces=[\"feeder\"]
Expand Down
Loading