Merge branch 'master' into bures-access

# Conflicts:
#	package.json
This commit is contained in:
witzig 2017-09-26 15:29:49 +02:00
commit 7a93628cc8
10 changed files with 108 additions and 52 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@
node_modules
npm-debug.log
package-lock.json
.DS_Store
config/development.*
config/production.*

View file

@ -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:

View file

@ -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
View file

@ -0,0 +1 @@
docs.mailtrain.org

1
docs/README.md Normal file
View file

@ -0,0 +1 @@
Here should be the docs

View file

@ -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"
}
}

View file

@ -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);
});

View file

@ -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
}
}]
}
});
});

View file

@ -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, {

View file

@ -48,7 +48,11 @@ let server = net.createServer(socket => {
if ( queued ) {
queued = queued[1];
queued_as = queued.match(/ queued as (\w+)/);
queued_as = queued_as[1];
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) => {