Merge branch 'master' into bures-access
# Conflicts: # package.json
This commit is contained in:
commit
7a93628cc8
10 changed files with 108 additions and 52 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
package-lock.json
|
||||
.DS_Store
|
||||
config/development.*
|
||||
config/production.*
|
||||
|
|
44
README.md
44
README.md
|
@ -119,6 +119,50 @@ If you are using the bundled ZoneMTA then you should make sure you are using a p
|
|||
|
||||
With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect 10/10 score out from [MailTester](https://www.mail-tester.com/) when sending a campaign message to a MailTester test address. I did not have VERP turned on, so the sender address matched return path address.
|
||||
|
||||
#### Getting your head around DKIM, DMARK, SPF and PTR
|
||||
|
||||
DKIM, DMARK, SPF and PTR are DNS records which spam filters use to figure out if e-mails were really sent by you (and not by a spammer who tries to conceal his identity to be able to continue send bulks of e-mails people never subscribed for). Assuming that you use zone-mta and your e-mails are to originate from a Mailtrain installation at `mailtrain.example.com` and optionally from `mail.example.net`, to practically set all these records up you will need to:
|
||||
|
||||
1. generate genrate a private and public DKIM key
|
||||
|
||||
```sh
|
||||
mkdir /opt/dkim-keys
|
||||
chmod 700 /opt/dkim-keys
|
||||
pushd /opt/dkim-keys
|
||||
openssl genrsa -out mailtrain.example.com.key 2048 # private key mailtrain.example.com.key
|
||||
openssl rsa -in mailtrain.example.com.key -out mailtrain.example.com.pub -pubout -outform PEM # public key mailtrain.example.com.pub
|
||||
```
|
||||
|
||||
2. add 3 new txt records for the mailtrain.example.com that will most likely similar to the example below:
|
||||
|
||||
```
|
||||
default._domainkey.mailtrain.example.com TXT "k=rsa; p=[public key in one line];"
|
||||
mailtrain.example.com TXT "v=spf1 mx a a:mail.example.net -all"
|
||||
_dmarc.mailtrain.example.com TXT "v=DMARC1; p=reject"
|
||||
```
|
||||
|
||||
(refer to a google search for a DKIM generator, SPF generator and DMARC genreator to get you up to speed). Configure your Mailtrain settings accoring to this:
|
||||
|
||||
**DKIM domain:** mailtrain.example.com
|
||||
**DKIM selector:** default
|
||||
**DKIM Private Key:** [copy and paste the private key in /opt/dkim-keys/mailtrain.example.com.key]
|
||||
|
||||
The above steps will have the following effect:
|
||||
|
||||
- all messages sent by Mailtrain / Zone-mta will be signed by the DKIM Private Key (the signature becomes a part of the e-mail)
|
||||
- when a spamfilter encounters this signature, it will look for the **<DKIM selector>**._domainkey.**<DKIM domain>** TXT record, and use the public key stored there to verify that the signature is valid
|
||||
- additionally, the spamfilter will look for a TXT SPF record and will look a if the e-mail was sent from the IP address of mailtrain.example.com or mail.example.net. If the sender IP or domain is different, it will discard the e-mail as spam.
|
||||
- furthermore, the spamfilter looks for the DMARC record, which tells it what to do with mails that aren't signed with DKIM or which don't have a valid signature. The example above will tell the spamfilter to reject such a mail as well.
|
||||
|
||||
3. You are now almost set. To further confirm that you have full control over your network, the last step is to set up a PTR record, which will give the right answer for a reverse DNS lookup (answer to "what domain name is bound to IP address xxx.xxx.xxx.xxx). If you run your own DNS, you probably know it will look similar to this:
|
||||
|
||||
```
|
||||
10.27/1.110.220.in-addr.arpa. 1800 PTR mailtrain.example.com.
|
||||
```
|
||||
|
||||
If you run Mailtrain on a VPS, you will have to find the PTR configuration somewhere in your administration interface or ask your provider to help you.
|
||||
|
||||
|
||||
### Simple Install (Docker)
|
||||
#### Requirements:
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ database="mailtrain"
|
|||
# MAMP users should also turn on "Allow network access to MySQL" otherwise MySQL might not be accessible
|
||||
port=3306
|
||||
charset="utf8mb4"
|
||||
# The timezone configured on the MySQL server. This can be 'local', 'Z', or an offset in the form +HH:MM or -HH:MM
|
||||
timezone="local"
|
||||
|
||||
[redis]
|
||||
|
|
1
docs/CNAME
Normal file
1
docs/CNAME
Normal file
|
@ -0,0 +1 @@
|
|||
docs.mailtrain.org
|
1
docs/README.md
Normal file
1
docs/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Here should be the docs
|
90
package.json
90
package.json
|
@ -28,95 +28,97 @@
|
|||
"node": ">=5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^7.2.3",
|
||||
"chai": "^4.0.2",
|
||||
"babel-eslint": "^8.0.0",
|
||||
"bluebird": "^3.5.0",
|
||||
"chai": "^4.1.2",
|
||||
"eslint-config-nodemailer": "^1.2.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-nodeunit": "^1.0.0",
|
||||
"grunt-eslint": "^20.0.0",
|
||||
"grunt-eslint": "^20.1.0",
|
||||
"jsxgettext-andris": "^0.9.0-patch.1",
|
||||
"mocha": "^3.3.0",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"selenium-webdriver": "^3.4.0",
|
||||
"lodash": "^4.17.4",
|
||||
"mocha": "^3.5.3",
|
||||
"phantomjs-prebuilt": "^2.1.15",
|
||||
"selenium-webdriver": "^3.5.0",
|
||||
"url-pattern": "^1.0.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"posix": "^4.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^2.3.0",
|
||||
"aws-sdk": "^2.37.0",
|
||||
"async": "^2.5.0",
|
||||
"aws-sdk": "^2.122.0",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"bluebird": "^3.5.0",
|
||||
"body-parser": "^1.17.1",
|
||||
"body-parser": "^1.18.2",
|
||||
"bounce-handler": "^7.3.2-fork.2",
|
||||
"compression": "^1.6.2",
|
||||
"config": "^1.25.1",
|
||||
"compression": "^1.7.0",
|
||||
"config": "^1.26.2",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-redis": "^3.2.0",
|
||||
"connect-redis": "^3.3.0",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"cors": "^2.8.3",
|
||||
"cors": "^2.8.4",
|
||||
"csurf": "^1.9.0",
|
||||
"csv-generate": "^1.0.0",
|
||||
"csv-parse": "^1.2.0",
|
||||
"csv-parse": "^1.2.3",
|
||||
"device": "^0.3.8",
|
||||
"dompurify": "^0.9.0",
|
||||
"dompurify": "^1.0.2",
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.15.2",
|
||||
"express-session": "^1.15.2",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"express": "^4.15.5",
|
||||
"express-session": "^1.15.5",
|
||||
"faker": "^4.1.0",
|
||||
"feedparser": "^2.1.0",
|
||||
"fs-extra": "^3.0.1",
|
||||
"feedparser": "^2.2.1",
|
||||
"fs-extra": "^4.0.2",
|
||||
"geoip-ultralight": "^0.1.5",
|
||||
"gettext-parser": "^1.2.2",
|
||||
"gettext-parser": "^1.3.0",
|
||||
"gm": "^1.23.0",
|
||||
"handlebars": "^4.0.6",
|
||||
"handlebars": "^4.0.10",
|
||||
"hbs": "^4.0.1",
|
||||
"he": "^1.1.1",
|
||||
"html-to-text": "^3.2.0",
|
||||
"html-to-text": "^3.3.0",
|
||||
"humanize": "0.0.9",
|
||||
"is-url": "^1.2.2",
|
||||
"isemail": "^2.2.1",
|
||||
"jquery-file-upload-middleware": "^0.1.8",
|
||||
"jsdom": "^9.12.0",
|
||||
"json-stringify-date": "^0.1.4",
|
||||
"juice": "^4.0.2",
|
||||
"juice": "^4.1.1",
|
||||
"knex": "^0.13.0",
|
||||
"libmime": "^3.1.0",
|
||||
"mailparser": "^2.0.5",
|
||||
"marked": "^0.3.6",
|
||||
"memory-cache": "^0.1.6",
|
||||
"mjml": "3.3.3",
|
||||
"memory-cache": "^0.2.0",
|
||||
"mjml": "3.3.5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment-timezone": "^0.5.12",
|
||||
"morgan": "^1.8.1",
|
||||
"moment-timezone": "^0.5.13",
|
||||
"morgan": "^1.8.2",
|
||||
"multer": "^1.3.0",
|
||||
"multiparty": "^4.1.3",
|
||||
"mysql2": "^1.3.5",
|
||||
"node-gettext": "^2.0.0-rc.1",
|
||||
"node-mocks-http": "^1.6.1",
|
||||
"node-mocks-http": "^1.6.5",
|
||||
"node-object-hash": "^1.2.0",
|
||||
"nodeify": "^1.0.1",
|
||||
"nodemailer": "^4.0.1",
|
||||
"nodemailer-openpgp": "^1.0.2",
|
||||
"npmlog": "^4.0.2",
|
||||
"object-hash": "^1.1.7",
|
||||
"openpgp": "^2.5.3",
|
||||
"nodemailer": "^4.1.1",
|
||||
"nodemailer-openpgp": "^1.1.0",
|
||||
"npmlog": "^4.1.2",
|
||||
"object-hash": "^1.1.8",
|
||||
"openpgp": "^2.5.11",
|
||||
"owasp-password-strength-test": "github:bures/owasp-password-strength-test",
|
||||
"passport": "^0.3.2",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"premailer-api": "^1.0.4",
|
||||
"redfour": "^1.0.0",
|
||||
"redis": "^2.7.1",
|
||||
"request": "^2.81.0",
|
||||
"request-promise": "^4.2.1",
|
||||
"serve-favicon": "^2.4.2",
|
||||
"redfour": "^1.0.2",
|
||||
"redis": "^2.8.0",
|
||||
"request": "^2.82.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"serve-favicon": "^2.4.4",
|
||||
"shortid": "^2.2.8",
|
||||
"slugify": "^1.1.0",
|
||||
"smtp-server": "^3.0.1",
|
||||
"striptags": "^3.0.1",
|
||||
"toml": "^2.3.2",
|
||||
"slugify": "^1.2.1",
|
||||
"smtp-server": "^3.1.0",
|
||||
"striptags": "^3.1.0",
|
||||
"toml": "^2.3.3",
|
||||
"try-require": "^1.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -563,7 +563,7 @@ router.get('/subscription/:id/import/:importId', passport.csrfProtection, (req,
|
|||
data.list = list;
|
||||
data.csrfToken = req.csrfToken();
|
||||
|
||||
data.customFields = fields.getRow(fieldList, data);
|
||||
data.customFields = fields.getRow(fieldList, data, false, true);
|
||||
|
||||
res.render('lists/subscription/import-preview', data);
|
||||
});
|
||||
|
|
|
@ -319,11 +319,11 @@ router.post('/zone-mta/sender-config', (req, res) => {
|
|||
|
||||
res.json({
|
||||
dkim: {
|
||||
keys: {
|
||||
keys: [{
|
||||
domainName: configItems.dkimDomain || domain,
|
||||
keySelector: configItems.dkimSelector,
|
||||
privateKey: configItems.dkimPrivateKey
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ function feedLoop() {
|
|||
return setTimeout(feedLoop, feed_timeout);
|
||||
}
|
||||
|
||||
let query = 'SELECT `id`, `source_url`, `from`, `address`, `subject`, `list`, `segment`, `html` FROM `campaigns` WHERE `type`=2 AND `status`=6 AND (`last_check` IS NULL OR `last_check`< NOW() - INTERVAL 10 MINUTE) LIMIT 1';
|
||||
let query = 'SELECT `id`, `source_url`, `from`, `address`, `subject`, `list`, `segment`, `html`, `open_tracking_disabled`, `click_tracking_disabled` FROM `campaigns` WHERE `type`=2 AND `status`=6 AND (`last_check` IS NULL OR `last_check`< NOW() - INTERVAL 10 MINUTE) LIMIT 1';
|
||||
|
||||
connection.query(query, (err, rows) => {
|
||||
connection.release();
|
||||
|
@ -148,7 +148,9 @@ function checkEntries(parent, entries, callback) {
|
|||
address: parent.address,
|
||||
subject: entry.title || parent.subject,
|
||||
list: parent.segment ? parent.list + ':' + parent.segment : parent.list,
|
||||
html
|
||||
html,
|
||||
openTrackingDisabled: parent.openTrackingDisabled,
|
||||
clickTrackingDisabled: parent.clickTrackingDisabled
|
||||
};
|
||||
|
||||
campaigns.create(campaign, {
|
||||
|
|
|
@ -48,7 +48,11 @@ let server = net.createServer(socket => {
|
|||
if ( queued ) {
|
||||
queued = queued[1];
|
||||
queued_as = queued.match(/ queued as (\w+)/);
|
||||
if (queued_as) {
|
||||
queued_as = queued_as[1];
|
||||
} else {
|
||||
queued_as = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,7 @@ let server = net.createServer(socket => {
|
|||
if (err || !message) {
|
||||
return checkNextLine();
|
||||
}
|
||||
if ( queued_as ) {
|
||||
if ( queued_as || status === 'sent' ) {
|
||||
log.verbose('POSTFIXBOUNCE', 'Message %s locally requeued as %s', queueId, queued_as);
|
||||
// Update message's previous queueId (thanks @mfechner )
|
||||
campaigns.updateMessageResponse(message, queued, queued_as, (err, updated) => {
|
||||
|
|
Loading…
Reference in a new issue