Added email debug, DNS MX check on all outbound emails.

Ylian Saint-Hilaire committed Mar 21, 2020 at 23:13 UTC 383f757e233395bfdce810eb0449d17558a81b28
4 files changed +117 -66
meshmail.js
+88 -44
@@ -137,74 +137,103 @@ module.exports.CreateMeshMail = function (parent) {
137
138 // Send account login mail / 2 factor token
139 obj.sendAccountLoginMail = function (domain, email, token) {
140 - var template = getTemplateEx('account-login', domain);
141 - if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, invitation not possible.
140 + obj.checkEmail(email, function (checked) {
141 + if (checked) {
142 + parent.debug('email', "Sending login token to " + email);
143
143 - // Set all the options.
144 - var options = { email: email, servername: domain.title ? domain.title : 'MeshCentral', token: token };
144 + var template = getTemplateEx('account-login', domain);
145 + if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, invitation not possible.
146
146 - // Send the email
147 - obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
148 - sendNextMail();
147 + // Set all the options.
148 + var options = { email: email, servername: domain.title ? domain.title : 'MeshCentral', token: token };
149 +
150 + // Send the email
151 + obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
152 + sendNextMail();
153 + }
154 + });
155 };
156
157 // Send account invitation mail
158 obj.sendAccountInviteMail = function (domain, username, accountname, email, password) {
153 - var template = getTemplateEx('account-invite', domain);
154 - if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, invitation not possible.
159 + obj.checkEmail(email, function (checked) {
160 + if (checked) {
161 + parent.debug('email', "Sending account invitation to " + email);
162
156 - // Set all the options.
157 - var options = { username: username, accountname: accountname, email: email, servername: domain.title ? domain.title : 'MeshCentral', password: password };
163 + var template = getTemplateEx('account-invite', domain);
164 + if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, invitation not possible.
165
159 - // Send the email
160 - obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
161 - sendNextMail();
166 + // Set all the options.
167 + var options = { username: username, accountname: accountname, email: email, servername: domain.title ? domain.title : 'MeshCentral', password: password };
168 +
169 + // Send the email
170 + obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
171 + sendNextMail();
172 + }
173 + });
174 };
175
176 // Send account check mail
177 obj.sendAccountCheckMail = function (domain, username, email) {
166 - var template = getTemplateEx('account-check', domain);
167 - if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, no reset possible.
178 + obj.checkEmail(email, function (checked) {
179 + if (checked) {
180 + parent.debug('email', "Sending email verification to " + email);
181
169 - // Set all the options.
170 - var options = { username: username, email: email, servername: domain.title ? domain.title : 'MeshCentral' };
171 - options.cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username.toLowerCase(), e: email, a: 1 }, obj.mailCookieEncryptionKey);
182 + var template = getTemplateEx('account-check', domain);
183 + if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, no reset possible.
184
173 - // Send the email
174 - obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
175 - sendNextMail();
185 + // Set all the options.
186 + var options = { username: username, email: email, servername: domain.title ? domain.title : 'MeshCentral' };
187 + options.cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username.toLowerCase(), e: email, a: 1 }, obj.mailCookieEncryptionKey);
188 +
189 + // Send the email
190 + obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
191 + sendNextMail();
192 + }
193 + });
194 };
195
196 // Send account reset mail
197 obj.sendAccountResetMail = function (domain, username, email) {
180 - var template = getTemplateEx('account-reset', domain);
181 - if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, don't validate the email address.
198 + obj.checkEmail(email, function (checked) {
199 + if (checked) {
200 + parent.debug('email', "Sending account password reset to " + email);
201
183 - // Set all the options.
184 - var options = { username: username, email: email, servername: domain.title ? domain.title : 'MeshCentral' };
185 - options.cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 }, obj.mailCookieEncryptionKey);
202 + var template = getTemplateEx('account-reset', domain);
203 + if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, don't validate the email address.
204
187 - // Send the email
188 - obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
189 - sendNextMail();
205 + // Set all the options.
206 + var options = { username: username, email: email, servername: domain.title ? domain.title : 'MeshCentral' };
207 + options.cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 }, obj.mailCookieEncryptionKey);
208 +
209 + // Send the email
210 + obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
211 + sendNextMail();
212 + }
213 + });
214 };
215
216 // Send agent invite mail
217 obj.sendAgentInviteMail = function (domain, username, email, meshid, name, os, msg, flags, expirehours) {
194 - var template = getTemplateEx('mesh-invite', domain);
195 - if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, don't validate the email address.
196 -
197 - // Set all the template replacement options and generate the final email text (both in txt and html formats).
198 - var options = { username: username, name: name, email: email, installflags: flags, msg: msg, meshid: meshid, meshidhex: meshid.split('/')[2], servername: domain.title ? domain.title : 'MeshCentral' };
199 - options.windows = ((os == 0) || (os == 1)) ? 1 : 0;
200 - options.linux = ((os == 0) || (os == 2)) ? 1 : 0;
201 - options.osx = ((os == 0) || (os == 3)) ? 1 : 0;
202 - options.link = (os == 4) ? 1 : 0;
203 - options.linkurl = createInviteLink(domain, meshid, flags, expirehours);
204 -
205 - // Send the email
206 - obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
207 - sendNextMail();
218 + obj.checkEmail(email, function (checked) {
219 + if (checked) {
220 + parent.debug('email', "Sending agent install invitation to " + email);
221 + var template = getTemplateEx('mesh-invite', domain);
222 + if ((template == null) || (template.htmlSubject == null) || (template.txtSubject == null) || (parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName.indexOf('.') == -1)) return; // If the server name is not set, don't validate the email address.
223 +
224 + // Set all the template replacement options and generate the final email text (both in txt and html formats).
225 + var options = { username: username, name: name, email: email, installflags: flags, msg: msg, meshid: meshid, meshidhex: meshid.split('/')[2], servername: domain.title ? domain.title : 'MeshCentral' };
226 + options.windows = ((os == 0) || (os == 1)) ? 1 : 0;
227 + options.linux = ((os == 0) || (os == 2)) ? 1 : 0;
228 + options.osx = ((os == 0) || (os == 3)) ? 1 : 0;
229 + options.link = (os == 4) ? 1 : 0;
230 + options.linkurl = createInviteLink(domain, meshid, flags, expirehours);
231 +
232 + // Send the email
233 + obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(template.htmlSubject, domain, options), text: mailReplacements(template.txt, domain, options), html: mailReplacements(template.html, domain, options) });
234 + sendNextMail();
235 + }
236 + });
237 };
238
239 // Send out the next mail in the pending list
@@ -223,6 +252,7 @@ module.exports.CreateMeshMail = function (parent) {
252 sendNextMail(); // Send the next mail
253 } else {
254 obj.retry++;
255 + parent.debug('email', 'SMTP server failed: ' + JSON.stringify(err));
256 console.log('SMTP server failed: ' + JSON.stringify(err));
257 if (obj.retry < 6) { setTimeout(sendNextMail, 60000); } // Wait and try again
258 }
@@ -238,6 +268,7 @@ module.exports.CreateMeshMail = function (parent) {
268 // Remove all non-object types from error to avoid a JSON stringify error.
269 var err2 = {};
270 for (var i in err) { if (typeof (err[i]) != 'object') { err2[i] = err[i]; } }
271 + parent.debug('email', 'SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err2));
272 console.log('SMTP mail server ' + parent.config.smtp.host + ' failed: ' + JSON.stringify(err2));
273 }
274 });
@@ -260,5 +291,18 @@ module.exports.CreateMeshMail = function (parent) {
291 return '/agentinvite?c=' + parent.encodeCookie({ a: 4, mid: meshid, f: flags, expire: expirehours * 60 }, parent.invitationLinkEncryptionKey);
292 }
293
294 + // Check the email domain DNS MX record.
295 + obj.approvedEmailDomains = {};
296 + obj.checkEmail = function (email, func) {
297 + var emailSplit = email.split('@');
298 + if (emailSplit.length != 2) { func(false); return; }
299 + if (obj.approvedEmailDomains[emailSplit[1]] === true) { func(true); return; }
300 + require('dns').resolveMx(emailSplit[1], function (err, addresses) {
301 + parent.debug('email', "checkEmail: " + email + ", " + (err == null));
302 + if (err == null) { obj.approvedEmailDomains[emailSplit[1]] = true; }
303 + func(err == null);
304 + });
305 + }
306 +
307 return obj;
308 };
\ No newline at end of file
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.5.0-q",
3 + "version": "0.5.0-r",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
translate/translate.json
+25 -19
@@ -3764,7 +3764,7 @@
3764 "pt": "Tem certeza de que deseja {0} o plug-in: {1}",
3765 "ru": "Вы уверенны, что {0} плагин: {1}",
3766 "xloc": [
3767 - "default.handlebars->27->1553"
3767 + "default.handlebars->27->1554"
3768 ],
3769 "zh-chs": "您確定要{0}插件嗎:{1}"
3770 },
@@ -4408,7 +4408,7 @@
4408 "pt": "Servidor CIRA",
4409 "ru": "CIRA Сервер",
4410 "xloc": [
4411 - "default.handlebars->27->1544"
4411 + "default.handlebars->27->1545"
4412 ],
4413 "zh-chs": "CIRA服務器"
4414 },
@@ -4425,7 +4425,7 @@
4425 "pt": "Comandos do servidor CIRA",
4426 "ru": "CIRA Сервер команды",
4427 "xloc": [
4428 - "default.handlebars->27->1545"
4428 + "default.handlebars->27->1546"
4429 ],
4430 "zh-chs": "CIRA服務器命令"
4431 },
@@ -4548,7 +4548,7 @@
4548 "pt": "Erro de chamada",
4549 "ru": "Ошибка вызова",
4550 "xloc": [
4551 - "default.handlebars->27->1554"
4551 + "default.handlebars->27->1555"
4552 ],
4553 "zh-chs": "通話錯誤"
4554 },
@@ -4966,7 +4966,7 @@
4966 "pt": "Verificando ...",
4967 "ru": "Проверка...",
4968 "xloc": [
4969 - "default.handlebars->27->1550",
4969 + "default.handlebars->27->1551",
4970 "default.handlebars->27->778"
4971 ],
4972 "zh-chs": "檢查..."
@@ -5824,7 +5824,7 @@
5824 "pt": "Encaminhador de conexão",
5825 "ru": "Ретранслятор подключения",
5826 "xloc": [
5827 - "default.handlebars->27->1543"
5827 + "default.handlebars->27->1544"
5828 ],
5829 "zh-chs": "連接繼電器"
5830 },
@@ -8723,6 +8723,12 @@
8723 ],
8724 "zh-chs": "郵件認證"
8725 },
8726 + {
8727 + "en": "Email Traffic",
8728 + "xloc": [
8729 + "default.handlebars->27->1538"
8730 + ]
8731 + },
8732 {
8733 "cs": "Ověření e-mailu",
8734 "de": "E-Mail-Verifizierung",
@@ -11386,7 +11392,7 @@
11392 "default.handlebars->27->1223",
11393 "default.handlebars->27->1229",
11394 "default.handlebars->27->1521",
11389 - "default.handlebars->27->1542"
11395 + "default.handlebars->27->1543"
11396 ],
11397 "zh-chs": "英特爾AMT"
11398 },
@@ -13186,7 +13192,7 @@
13192 "pt": "Menos",
13193 "ru": "Меньше",
13194 "xloc": [
13189 - "default.handlebars->27->1556"
13195 + "default.handlebars->27->1557"
13196 ],
13197 "zh-chs": "減"
13198 },
@@ -15252,7 +15258,7 @@
15258 "pt": "Mais",
15259 "ru": "Еще",
15260 "xloc": [
15255 - "default.handlebars->27->1555"
15261 + "default.handlebars->27->1556"
15262 ],
15263 "zh-chs": "更多"
15264 },
@@ -17996,7 +18002,7 @@
18002 "pt": "Ação do Plugin",
18003 "ru": "Действие плагина",
18004 "xloc": [
17999 - "default.handlebars->27->1552",
18005 + "default.handlebars->27->1553",
18006 "default.handlebars->27->159"
18007 ],
18008 "zh-chs": "插件動作"
@@ -20738,7 +20744,7 @@
20744 "pt": "Rastreamento de servidor",
20745 "ru": "Трассировка сервера",
20746 "xloc": [
20741 - "default.handlebars->27->1546"
20747 + "default.handlebars->27->1547"
20748 ],
20749 "zh-chs": "服務器跟踪"
20750 },
@@ -24177,7 +24183,7 @@
24183 "pt": "Atualizado",
24184 "ru": "Актуально",
24185 "xloc": [
24180 - "default.handlebars->27->1551"
24186 + "default.handlebars->27->1552"
24187 ],
24188 "zh-chs": "最新"
24189 },
@@ -25251,8 +25257,8 @@
25257 "pt": "Servidor web",
25258 "ru": "Веб-сервер",
25259 "xloc": [
25254 - "default.handlebars->27->1538",
25255 - "default.handlebars->27->1539"
25260 + "default.handlebars->27->1539",
25261 + "default.handlebars->27->1540"
25262 ],
25263 "zh-chs": "網絡服務器"
25264 },
@@ -25269,7 +25275,7 @@
25275 "pt": "Solicitações de servidor Web",
25276 "ru": "Запросы веб-сервера",
25277 "xloc": [
25272 - "default.handlebars->27->1540"
25278 + "default.handlebars->27->1541"
25279 ],
25280 "zh-chs": "Web服務器請求"
25281 },
@@ -25286,7 +25292,7 @@
25292 "pt": "Encaminhador de soquete da Web",
25293 "ru": "Ретранслятор Web Socket",
25294 "xloc": [
25289 - "default.handlebars->27->1541"
25295 + "default.handlebars->27->1542"
25296 ],
25297 "zh-chs": "Web套接字中繼"
25298 },
@@ -26123,7 +26129,7 @@
26129 "pt": "\\\\'",
26130 "ru": "\\\\'",
26131 "xloc": [
26126 - "default.handlebars->27->1549"
26132 + "default.handlebars->27->1550"
26133 ],
26134 "zh-chs": "\\\\'"
26135 },
@@ -26656,7 +26662,7 @@
26662 "pt": "servertrace.csv",
26663 "ru": "servertrace.csv",
26664 "xloc": [
26659 - "default.handlebars->27->1548"
26665 + "default.handlebars->27->1549"
26666 ],
26667 "zh-chs": "servertrace.csv"
26668 },
@@ -26722,7 +26728,7 @@
26728 "pt": "hora, fonte, mensagem",
26729 "ru": "time, source, message",
26730 "xloc": [
26725 - "default.handlebars->27->1547"
26731 + "default.handlebars->27->1548"
26732 ],
26733 "zh-chs": "時間,來源,訊息"
26734 },
views/default.handlebars
+3 -2
@@ -10805,6 +10805,7 @@
10805 x += '<div><label><input type=checkbox id=p41c14 ' + ((serverTraceSources.indexOf('agentupdate') >= 0) ? 'checked' : '') + '>' + "MeshAgent update" + '</label></div>';
10806 x += '<div><label><input type=checkbox id=p41c16 ' + ((serverTraceSources.indexOf('cert') >= 0) ? 'checked' : '') + '>' + "Server Certificate" + '</label></div>';
10807 x += '<div><label><input type=checkbox id=p41c17 ' + ((serverTraceSources.indexOf('db') >= 0) ? 'checked' : '') + '>' + "Server Database" + '</label></div>';
10808 + x += '<div><label><input type=checkbox id=p41c18 ' + ((serverTraceSources.indexOf('email') >= 0) ? 'checked' : '') + '>' + "Email Traffic" + '</label></div>';
10809 x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>' + "Web Server" + '</b></div>';
10810 x += '<div><label><input type=checkbox id=p41c5 ' + ((serverTraceSources.indexOf('web') >= 0) ? 'checked' : '') + '>' + "Web Server" + '</label></div>';
10811 x += '<div><label><input type=checkbox id=p41c6 ' + ((serverTraceSources.indexOf('webrequest') >= 0) ? 'checked' : '') + '>' + "Web Server Requests" + '</label></div>';
@@ -10821,8 +10822,8 @@
10822 }
10823
10824 function setServerTracingEx(b) {
10824 - var sources = [], allsources = ['cookie', 'dispatch', 'main', 'peer', 'web', 'webrequest', 'relay', 'webrelaydata', 'webrelay', 'mps', 'mpscmd', 'swarm', 'swarmcmd', 'agentupdate', 'agent', 'cert', 'db'];
10825 - if (b == 1) { for (var i = 1; i < 18; i++) { try { if (Q('p41c' + i).checked) { sources.push(allsources[i - 1]); } } catch (ex) { } } }
10825 + var sources = [], allsources = ['cookie', 'dispatch', 'main', 'peer', 'web', 'webrequest', 'relay', 'webrelaydata', 'webrelay', 'mps', 'mpscmd', 'swarm', 'swarmcmd', 'agentupdate', 'agent', 'cert', 'db', 'email'];
10826 + if (b == 1) { for (var i = 1; i < 19; i++) { try { if (Q('p41c' + i).checked) { sources.push(allsources[i - 1]); } } catch (ex) { } } }
10827 meshserver.send({ action: 'traceinfo', traceSources: sources });
10828 }
10829