From 0e63d517ad88ce0106717ae28af818478626f78a Mon Sep 17 00:00:00 2001 From: Marius Petcu Date: Thu, 19 Sep 2019 09:46:21 -0400 Subject: [PATCH 1/5] Make IP address configurable to allow operation behind a NAT --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index b69a4a5b..08b33eb1 100644 --- a/index.js +++ b/index.js @@ -461,7 +461,7 @@ class ForwardEmail { // get the fully qualified domain name ("FQDN") of this server const ipAddress = - process.env.NODE_ENV === 'test' ? '178.128.149.101' : ip.address(); + process.env.NODE_ENV === 'test' ? '178.128.149.101' : (this.config.ipAddress || ip.address()); const name = process.env.NODE_ENV === 'test' ? 'mx1.forwardemail.net' From 02ede87f33a3edb9f6ae563bb2ae74bbafda7fae Mon Sep 17 00:00:00 2001 From: Marius Petcu Date: Thu, 19 Sep 2019 11:39:01 -0400 Subject: [PATCH 2/5] Expose more config options via environment variables --- .gitignore | 1 + index.js | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) mode change 100644 => 100755 index.js diff --git a/.gitignore b/.gitignore index 964c1af4..bd2794a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store *.log +*.sw* .idea node_modules coverage diff --git a/index.js b/index.js old mode 100644 new mode 100755 index 08b33eb1..b051d16e --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + const crypto = require('crypto'); const dns = require('dns'); const fs = require('fs'); @@ -1110,25 +1112,34 @@ class ForwardEmail { } if (!module.parent) { + const hostname = process.env.HOSTNAME || 'forwardemail.net'; + const defaultDeployment = hostname === 'forwardemail.net'; + const config = { - noReply: 'no-reply@forwardemail.net', - exchanges: ['mx1.forwardemail.net', 'mx2.forwardemail.net'], + noReply: `no-reply@${hostname}`, + exchanges: process.env.EXCHANGES + ? process.env.EXCHANGES.split(',') + : (defaultDeployment ? ['mx1.forwardemail.net', 'mx2.forwardemail.net'] : [hostname]), ssl: {}, - dkim: {} + dkim: {}, + ipAddress: process.env.IP_ADDRESS || undefined, }; - if (process.env.NODE_ENV === 'production') { - // needsUpgrade = true; + + if (process.env.SECURE === 'true') { config.ssl = { - secure: process.env.SECURE === 'true', - key: fs.readFileSync('/home/deploy/mx1.forwardemail.net.key', 'utf8'), - cert: fs.readFileSync('/home/deploy/mx1.forwardemail.net.cert', 'utf8'), - ca: fs.readFileSync('/home/deploy/mx1.forwardemail.net.ca', 'utf8') + secure: true, + key: fs.readFileSync(process.env.SSL_KEY || '/home/deploy/mx1.forwardemail.net.key', 'utf8'), + cert: fs.readFileSync(process.env.SSL_CERT || '/home/deploy/mx1.forwardemail.net.cert', 'utf8'), + ca: fs.readFileSync(proicess.env.SSL_CA || '/home/deploy/mx1.forwardemail.net.ca', 'utf8') }; + } + + if (process.env.DKIM_PRIVATE_KEY || (defaultDeployment && process.env.NODE_ENV === 'production')) { config.dkim = { - domainName: 'forwardemail.net', + domainName: hostname, keySelector: 'default', - privateKey: fs.readFileSync('/home/deploy/dkim-private.key', 'utf8'), + privateKey: fs.readFileSync(process.env.DKIM_PRIVATE_KEY || '/home/deploy/dkim-private.key', 'utf8'), cacheDir: os.tmpdir() }; } From 7bc1f280b87589f311d3979d106e30fa4309fbdf Mon Sep 17 00:00:00 2001 From: Marius Petcu Date: Thu, 19 Sep 2019 11:39:32 -0400 Subject: [PATCH 3/5] Add Dockerfile --- .dockerignore | 14 ++++++++++++++ Dockerfile | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..20982fd9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.DS_Store +*.sw* +*.log +.idea +node_modules +coverage +.nyc_output +dkim-public.key +dkim-private.key +mx1.forwardemail.net.ca +mx1.forwardemail.net.cert +mx1.forwardemail.net.csr +mx1.forwardemail.net.key +package-lock.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..4708fefd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +FROM node:10-stretch + +WORKDIR /app + +RUN \ + apt-get -y update && \ + apt-get -y upgrade && \ + apt-get install -y software-properties-common redis-server spamassassin spamc python openssl python-pip curl && \ + pip install pyspf pydns ipaddr dkimpy pynacl authres dnspython pydns + +RUN \ + openssl genrsa -out dkim-private.key 1024 && \ + openssl rsa -in dkim-private.key -pubout -out dkim-public.key && \ + echo "Add this to your DNS zonefile:" && \ + sed '3,3!d' dkim-public.key | sed ':a;N;$!ba;s/\n//g' | xargs -I{} echo "default._domainkey 14400 IN TXT \"v=DKIM1; k=rsa; p={}\"" | tee DKIM-TXT-record + +COPY package.json yarn.lock /app/ +RUN yarn + +COPY * /app/ + +EXPOSE 25 + +ENV IP_ADDRESS "" +ENV EXCHANGES "" +ENV DKIM_PRIVATE_KEY /app/dkim-private.key + +CMD \ + /usr/sbin/spamd -d --pidfile=/var/run/spamd.pid && \ + /usr/bin/redis-server /etc/redis/redis.conf && \ + /app/index.js + +#docker run -e --network host --hostname --name -d forward-email:latest + +# Add the following line to your DNS zonefile: +#docker exec cat /app/DKIM-TXT-record From d4b6a3f2987d9285b28a0b146427478119ff7fdf Mon Sep 17 00:00:00 2001 From: Marius Petcu Date: Thu, 19 Sep 2019 19:19:51 +0300 Subject: [PATCH 4/5] Document Docker building and running --- Dockerfile | 2 +- README.md | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4708fefd..82d0ea3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ CMD \ /usr/bin/redis-server /etc/redis/redis.conf && \ /app/index.js -#docker run -e --network host --hostname --name -d forward-email:latest +#docker run --network host --hostname --name -d forward-email:latest # Add the following line to your DNS zonefile: #docker exec cat /app/DKIM-TXT-record diff --git a/README.md b/README.md index e9855af6..1afc3729 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,19 @@ You'll also need the following dependencies installed: * Nameservers - we highly recommend you set your server's nameservers to `1.1.1.1` (see ["How do you perform DNS lookups on domain names"](#how-do-you-perform-dns-lookups-on-domain-names) below and here is a [Digital Ocean guide][do-guide]) +### Docker deployment + +Instead of installing the server and all its dependencies manually on a server, +you can use [Docker](https://docker.com). + +Clone this repo, then: + +```bash +docker build -t forward-email:latest . +docker run --network host --hostname --name -d forward-email:latest +``` + +Then, you'll need to add the DKIM public key that was generated for you as a TXT record to your zonefile. Run ```docker exec cat /app/DKIM-TXT-record``` to print it out. ## Programmatic Usage @@ -606,7 +619,7 @@ At no point in time do we write to disk or store emails – everything is done i [MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com/) -## +## [npm]: https://www.npmjs.com/ From 38b9b73135b7c5b3051bdc8412233a7b178b5312 Mon Sep 17 00:00:00 2001 From: Marius Petcu Date: Fri, 20 Sep 2019 11:31:53 +0300 Subject: [PATCH 5/5] Allow SSL (and DKIM) key configuration via env vars --- Dockerfile | 11 ++++++++++- index.js | 15 ++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 82d0ea3e..73917c5c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,16 @@ EXPOSE 25 ENV IP_ADDRESS "" ENV EXCHANGES "" -ENV DKIM_PRIVATE_KEY /app/dkim-private.key +ENV SECURE "false" +ENV SSL_KEY "" +ENV SSL_KEY_FILE "" +ENV SSL_CERT "" +ENV SSL_CERT_FILE "" +ENV SSL_CA "" +ENV SSL_CA_FILE "" +ENV DKIM_PRIVATE_KEY "" +ENV DKIM_KEY_SELECTOR "default" +ENV DKIM_PRIVATE_KEY_FILE /app/dkim-private.key CMD \ /usr/sbin/spamd -d --pidfile=/var/run/spamd.pid && \ diff --git a/index.js b/index.js index b051d16e..951c4dc7 100755 --- a/index.js +++ b/index.js @@ -1117,7 +1117,7 @@ if (!module.parent) { const config = { noReply: `no-reply@${hostname}`, - exchanges: process.env.EXCHANGES + exchanges: process.env.EXCHANGES ? process.env.EXCHANGES.split(',') : (defaultDeployment ? ['mx1.forwardemail.net', 'mx2.forwardemail.net'] : [hostname]), ssl: {}, @@ -1125,21 +1125,22 @@ if (!module.parent) { ipAddress: process.env.IP_ADDRESS || undefined, }; + const readKey = (key, keyFile) => key || (keyFile && fs.readFileSync(keyFile, 'utf8')) || undefined; if (process.env.SECURE === 'true') { config.ssl = { secure: true, - key: fs.readFileSync(process.env.SSL_KEY || '/home/deploy/mx1.forwardemail.net.key', 'utf8'), - cert: fs.readFileSync(process.env.SSL_CERT || '/home/deploy/mx1.forwardemail.net.cert', 'utf8'), - ca: fs.readFileSync(proicess.env.SSL_CA || '/home/deploy/mx1.forwardemail.net.ca', 'utf8') + key: readKey(process.env.SSL_KEY, process.env.SSL_KEY_FILE || '/home/deploy/mx1.forwardemail.net.key'), + cert: readKey(process.env.SSL_CERT, process.env.SSL_CERT_FILE || '/home/deploy/mx1.forwardemail.net.cert'), + ca: readKey(process.env.SSL_CA, process.env.SSL_CA_FILE || '/home/deploy/mx1.forwardemail.net.ca'), }; } - if (process.env.DKIM_PRIVATE_KEY || (defaultDeployment && process.env.NODE_ENV === 'production')) { + if (process.env.DKIM_PRIVATE_KEY || process.env.DKIM_PRIVATE_KEY_FILE || (defaultDeployment && process.env.NODE_ENV === 'production')) { config.dkim = { domainName: hostname, - keySelector: 'default', - privateKey: fs.readFileSync(process.env.DKIM_PRIVATE_KEY || '/home/deploy/dkim-private.key', 'utf8'), + keySelector: process.env.DKIM_KEY_SELECTOR || 'default', + privateKey: readKey(process.env.DKIM_PRIVATE_KEY, process.env.DKIM_PRIVATE_KEY_FILE || '/home/deploy/dkim-private.key'), cacheDir: os.tmpdir() }; }