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

Allow custom paths for certificates #36

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
13 changes: 12 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM sillelien/base-alpine:0.10
MAINTAINER Zhuohuan LI <[email protected]>

ENV BATS_VERSION 0.4.0
ENV DOCKERIZE_VERSION v0.6.0

## Install System

Expand All @@ -19,11 +20,19 @@ RUN apk update && apk add \
&& tar -xzf "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ \
&& bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local \
\
&& wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
\
&& rm -rf /var/cache/apk/* && rm -rf /tmp/*


## Configure Service

COPY install/main.dist.cf /etc/postfix/main.cf
ENV CERTIFICATE_PUBLIC=/etc/postfix/cert/smtp.cert
ENV CERTIFICATE_PRIVATE=/etc/postfix/cert/smtp.key
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we support inline data here?

For example:

read -r -d '' CERTIFICATE_PUBLIC <<'EOF'
... CERT
... HERE
EOF

This would be able to let me set it in docker cloud web manager directly, without care about any files.

Copy link
Contributor Author

@bcardiff bcardiff Mar 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a bit hacky probably. How would you differentiate between a path and inline data? Because it starts with a /? I would rather use another variable and in the init-openssl.sh echo a CERTIFICATE_PUBLIC_PAYLOAD to the CERTIFICATE_PUBLIC path. Same with private.

It's rare that both CERTIFICATE_PUBLIC and CERTIFICATE_PUBLIC_PAYLOAD be defined at the same time. But I think different variables would end up in cleaner expectation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On more thought, is it realistic to manually edit the certificate content? In my use case either in docker cloud or in rancher a letsencrypt container periodically renew the certificate. Been able to mount the output as is in the smf container is enough to keep things working.

If a renewal of the certificate is the motivation for this, it will force you to manually be involved.

And on other side, isn't it to risky to allow save the private key in the database of the docker cloud manager you use?

I think that easy manual editing of the content of the certificate can be obtained by this PR as is plus using for example https://filebrowser.github.io/ which can be used as a docker container and has inline file editing.


COPY install/main.dist.cf.tmpl /etc/postfix/main.cf.tmpl
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to overwrite this file(main.cf) every time if CERTIFICATE_PUBLIC is set because that's the purpose of why we set the ENV variable.

Copy link
Contributor Author

@bcardiff bcardiff Mar 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default the content of the expanded main.cf will be "overwritten" because the location of /etc/postfix is not mounted as a volume, hence it will be lost between restarts.

But if the user mounts the /etc/postfix/main.cf I don't think is desirable to overwrite it. It's more about not blocking eventual needed tweaks in that file.

COPY install/master.dist.cf /etc/postfix/master.cf

RUN cat /dev/null > /etc/postfix/aliases && newaliases \
Expand Down Expand Up @@ -53,6 +62,8 @@ COPY .git/logs/HEAD /app/GIT_LOG
COPY .git/HEAD /app/GIT_HEAD
COPY install/buildenv.sh /app/

RUN rm /etc/postfix/main.cf

VOLUME ["/var/spool/postfix"]

EXPOSE 25
Expand Down
31 changes: 21 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ This docker was built for ultimate **simplicity** because of this reason. I owne

### Related Services
- [DuoCircle Email Forwarding](http://www.duocircle.com/services/email-forwarding) From $59.95/year
- [Cloud Mail In](https://www.cloudmailin.com/plans) From $9/month. And it is not for human.
- [Cloud Mail In](https://www.cloudmailin.com/plans) From $9/month. And it is not for human.
- [MailGun](https://mailgun.com) professional service. Free plan includes 10,000 emails/month. but [can result in your domain being treated as spam](https://blog.rajivm.net/mailgun-forwarding-spam.html)

I was willing to pay $10/year, but the cheapest plan I could find was $9 per month. Having a $10 USD machine with unlimited mail&domains per month is an amazing idea! And of couse you could also put other dockers on this same machine. :-D
Expand All @@ -57,7 +57,7 @@ If you want to forward all emails sent to domain testo.com to [email protected], set
$ export SMF_CONFIG='@testo.com:[email protected]'
```

See? There is nothing easier.
See? There is nothing easier.

> If you want to run it constanly in the background add ` -td` after `run`:
```bash
Expand Down Expand Up @@ -149,20 +149,30 @@ Send all outgoing mail trough a smarthost on 192.168.1.2
$ export SMF_RELAYHOST='192.168.1.2'
```

### `CERTIFICATE_PUBLIC`, `CERTIFICATE_PRIVATE` Examples

Path of the certificate and private key. Defaults:

* `CERTIFICATE_PUBLIC` = `/etc/postfix/cert/smtp.cert`
* `CERTIFICATE_PRIVATE` = `/etc/postfix/cert/smtp.key`

**Note:** Although full paths are specified for each file they must be in the same directory.

If files do no exist on boot a self signed certificate is created. Read the following section for more information.

TLS (SSL) Certificates
--------------------
SMF creates its own certificate and private key when starts. However, this certificate is self signed and so some systems might give you a warning about the server not being trusted.
If you have valid certificates for the domain name of the host, then you can use them and avoid the warning about not being trusted.

1. First you need to prepare the certificate files. Copy your full chain certificate to a file named `smtp.cert`. Then copy the private key to a file named `smtp.key`

2. Copy these files to a folder. For example: `/data/certs/`. This folder will be mounted as a volume in SMF
1. First you need to prepare the certificate files. Identify your full chain certificate and private key. They are usually next to each other. Mount the directory for example at `/data/certs`. Let's assume `/data/certs/smtp.cert` is the full chain certificat and `/data/certs/smtp.key` is the private key in your host running docker.

3. When creating the container, add the `-v` (volume) parameter to mount it to the folder `/etc/postfix/cert/` like so:
2. When creating the container, add the `-v` (volume) parameter to mount as a folder `/certificate` and specify the path of them in the container like so:
```bash
$ docker run -e SMF_CONFIG="$SMF_CONFIG" -p 25:25 -v /data/certs/:/etc/postfix/cert/ zixia/simple-mail-forwarder
$ docker run -e SMF_CONFIG="$SMF_CONFIG" -e CERTIFICATE_PUBLIC="/certificate/smtp.cert" -e CERTIFICATE_PRIVATE="/certificate/smtp.key" -p 25:25 -v /data/certs:/certificate zixia/simple-mail-forwarder
```
4. Your emails should now be forwarded with trusted encryption. You can use this tool to test it: <a href="http://checktls.com/" target="_blank">http://checktls.com/</a>

3. Your emails should now be forwarded with trusted encryption. You can use this tool to test it: <a href="http://checktls.com/" target="_blank">http://checktls.com/</a>

If you do not have a certificate and don't have the $$ to buy one, you can use <a href="https://letsencrypt.org" target="_blank">https://letsencrypt.org</a> if you have shell access to the server (Notice, SMF does not provide, yet, this service). Letsencrypt allows you to create valid trusted certificates for a server, if the server responds to the domain you specify. In order to do this, you need to run the program from within the server and have administrator rights.

Expand All @@ -179,9 +189,10 @@ If you do not have a certificate and don't have the $$ to buy one, you can use <
```bash
$ letsencrypt certonly --standalone -d yourdomain.com -d www.yourdomain.com -d mail.yourdomain.com
```
5. Follow the prompts and if everything is successful you will get your certificates in a folder like `/etc/letsencrypt/live/mydomain.com`

6. You can now use those certificates to make SMF TLS trusted.
5. Follow the prompts and if everything is successful you will get your certificates in a folder like `/etc/letsencrypt/live/mydomain.com`.

6. You can now use those certificates to make SMF TLS trusted. You will need to use `fullchain.pem` and `privkey.pem` as the certificate and private key respectively.

> This was a quick way of how to use letsencrypt. For a full tutorial based on your OS see: <a href="https://certbot.eff.org/" tareget="_blank">https://certbot.eff.org/</a>

Expand Down
2 changes: 2 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/bash
ARGV=$@

dockerize -no-overwrite -template /etc/postfix/main.cf.tmpl:/etc/postfix/main.cf

function print_help {
cat <<EOF
Docker SMF - Simple Mail Forwarder
Expand Down
16 changes: 10 additions & 6 deletions install/init-openssl.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
#!/bin/bash

[ -d /etc/postfix/cert ] || {
mkdir -p /etc/postfix/cert
CERTIFICATE_DIR=$(dirname $CERTIFICATE_PUBLIC)
CERTIFICATE_PUBLIC_FILE=$(basename $CERTIFICATE_PUBLIC)
CERTIFICATE_PRIVATE_FILE=$(basename $CERTIFICATE_PRIVATE)

[ -d $CERTIFICATE_DIR ] || {
mkdir -p $CERTIFICATE_DIR
}

cd /etc/postfix/cert
cd $CERTIFICATE_DIR
# skip generation of certificate if one exists (by mounting a volume)
if [ ! -f "smtp.cert" ]; then
if [ ! -f ${CERTIFICATE_PUBLIC} ]; then
#openssl dhparam -2 -out dh_512.pem 512
#openssl dhparam -2 -out dh_1024.pem 1024
openssl req -new -outform PEM -out smtp.cert -newkey rsa:2048 \
-nodes -keyout smtp.key -keyform PEM -days 3650 -x509 \
openssl req -new -outform PEM -out $CERTIFICATE_PUBLIC_FILE -newkey rsa:2048 \
-nodes -keyout $CERTIFICATE_PRIVATE_FILE -keyform PEM -days 3650 -x509 \
-subj "/C=US/ST=Matrix/L=L/O=O/CN=${SMF_DOMAIN:-simple-mail-forwarder.com}"

chown -R root.postfix /etc/postfix/cert/
Expand Down
4 changes: 2 additions & 2 deletions install/main.dist.cf → install/main.dist.cf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,10 @@ smtpd_tls_auth_only = no
# smtpd_tls_CAfile = /etc/postfix/cert/example-cacert.pem

# Public Certificate
smtpd_tls_cert_file = /etc/postfix/cert/smtp.cert
smtpd_tls_cert_file = {{ .Env.CERTIFICATE_PUBLIC }}

# Private Key (without passphrase)
smtpd_tls_key_file = /etc/postfix/cert/smtp.key
smtpd_tls_key_file = {{ .Env.CERTIFICATE_PRIVATE }}

# Randomizer for key creation
tls_random_source = dev:/dev/urandom
Expand Down