Merge pull request #724 from Nebukadneza/traefik_support

Documentation and examples for traefik
master
Tim Möhlmann 6 years ago committed by GitHub
commit 7eff09a74b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,143 @@
version: '2'
services:
# This would normally not be here, but where you define your system services
traefik:
image: traefik:alpine
command: --docker
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/data/traefik/acme.json:/acme.json"
- "/data/traefik/traefik.toml:/traefik.toml"
# This may be needed (plus defining mailu_default external: true) if traefik lives elsewhere
# networks:
# - mailu_default
certdumper:
restart: always
image: mailu/traefik-certdumper:$VERSION
environment:
# Make sure this is the same as the main=-domain in traefik.toml
# !!! Also dont forget to add "TRAEFIK_DOMAIN=[...]" to your .env!
- DOMAIN=$TRAEFIK_DOMAIN
volumes:
- "/data/traefik:/traefik"
- "$ROOT/certs:/output"
front:
image: mailu/nginx:$VERSION
restart: always
env_file: .env
logging:
driver: $LOG_DRIVER
labels: # Traefik labels for simple reverse-proxying
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:$TRAEFIK_DOMAIN"
- "traefik.docker.network=mailu_default"
ports:
- "$BIND_ADDRESS4:110:110"
- "$BIND_ADDRESS4:143:143"
- "$BIND_ADDRESS4:993:993"
- "$BIND_ADDRESS4:995:995"
- "$BIND_ADDRESS4:25:25"
- "$BIND_ADDRESS4:465:465"
- "$BIND_ADDRESS4:587:587"
- "$BIND_ADDRESS6:110:110"
- "$BIND_ADDRESS6:143:143"
- "$BIND_ADDRESS6:993:993"
- "$BIND_ADDRESS6:995:995"
- "$BIND_ADDRESS6:25:25"
- "$BIND_ADDRESS6:465:465"
- "$BIND_ADDRESS6:587:587"
volumes:
- "$ROOT/overrides/nginx:/overrides"
- /data/traefik/ssl/$TRAEFIK_DOMAIN.crt:/certs/cert.pem
- /data/traefik/ssl/$TRAEFIK_DOMAIN.key:/certs/key.pem
redis:
image: redis:alpine
restart: always
volumes:
- "$ROOT/redis:/data"
imap:
image: mailu/dovecot:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/mail:/mail"
- "$ROOT/overrides:/overrides"
depends_on:
- front
smtp:
image: mailu/postfix:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/overrides:/overrides"
depends_on:
- front
antispam:
image: mailu/rspamd:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/filter:/var/lib/rspamd"
- "$ROOT/dkim:/dkim"
- "$ROOT/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
antivirus:
image: mailu/$ANTIVIRUS:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/filter:/data"
webdav:
image: mailu/$WEBDAV:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/dav:/data"
admin:
image: mailu/admin:$VERSION
restart: always
env_file: .env
volumes:
- "$ROOT/data:/data"
- "$ROOT/dkim:/dkim"
depends_on:
- redis
webmail:
image: "mailu/$WEBMAIL:$VERSION"
restart: always
env_file: .env
volumes:
- "$ROOT/webmail:/data"
depends_on:
- imap
fetchmail:
image: mailu/fetchmail:$VERSION
restart: always
env_file: .env
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: $SUBNET

@ -0,0 +1,33 @@
# This is just boilerplate stuff you probably have in your own config
logLevel = "INFO"
defaultEntryPoints = ["https","http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[docker]
endpoint = "unix:///var/run/docker.sock"
watch = true
exposedByDefault = false
# Make sure we get acme.json saved, and onHostRule enabled
[acme]
email = "your@mail.tld"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
# This should include all of your mail domains, and main= should be your $TRAEFIK_DOMAIN
[[acme.domains]]
main = "mail.your.doma.in"
sans = ["web.mail.your.doma.in", "smtp.mail.doma.in", "imap.mail.doma.in"]

@ -8,6 +8,7 @@ In such a configuration, one would usually run a frontend reverse proxy to serve
There are basically three options, from the most to the least recommended one: There are basically three options, from the most to the least recommended one:
- have Mailu Web frontend listen locally and use your own Web frontend on top of it - have Mailu Web frontend listen locally and use your own Web frontend on top of it
- use ``Traefik`` in another container as central system-reverse-proxy
- override Mailu Web frontend configuration - override Mailu Web frontend configuration
- disable Mailu Web frontend completely and use your own - disable Mailu Web frontend completely and use your own
@ -114,11 +115,87 @@ Depending on how you access the front server, you might want to add a ``proxy_re
This will stop redirects (301 and 302) sent by the Webmail, nginx front and admin interface from sending you to ``localhost``. This will stop redirects (301 and 302) sent by the Webmail, nginx front and admin interface from sending you to ``localhost``.
Use Traefik in another container as central system-reverse-proxy
--------------------------------------------------------------------
`Traefik`_ is a popular reverse-proxy aimed at containerized systems.
As such, many may wish to integrate Mailu into a system which already uses Traefik as its sole ingress/reverse-proxy.
As the ``mailu/front`` container uses Nginx not only for ``HTTP`` forwarding, but also for the mail-protocols like ``SMTP``, ``IMAP``, etc, we need to keep this
container around even when using another ``HTTP`` reverse-proxy. Furthermore, Traefik is neither able to forward non-HTTP, nor can it easily forward HTTPS-to-HTTPS.
This, however, means 3 things:
- ``mailu/front`` needs to listen internally on ``HTTP`` rather than ``HTTPS``
- ``mailu/front`` is not exposed to the outside world on ``HTTP``
- ``mailu/front`` still needs ``SSL`` certificates (here, we assume ``letsencrypt``) for a well-behaved mail service
This makes the setup with Traefik a bit harder: Traefik saves its certificates in a proprietary *JSON* file, which is not readable by Nginx in the ``front``-container.
To solve this, your ``acme.json`` needs to be exposed to the host or a ``docker-volume``. It will then be read by a script in another container,
which will dump the certificates as ``PEM`` files, readable for Nginx. The ``front`` container will automatically reload Nginx whenever these certificates change.
To set this up, first set ``TLS_FLAVOR=mail`` in your ``.env``. This tells ``mailu/front`` not to try to request certificates using ``letsencrypt``,
but to read provided certificates, and use them only for mail-protocols, not for ``HTTP``.
Next, in your ``docker-compose.yml``, comment out the ``port`` lines of the ``front`` section for port ``…:80`` and ``…:440``.
Add the respective Traefik labels for your domain/configuration, like
.. code-block:: yaml
labels:
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:$TRAEFIK_DOMAIN"
.. note:: Please dont forget to add ``TRAEFIK_DOMAIN=[...]`` TO YOUR ``.env``
If your Traefik is configured to automatically request certificates from *letsencrypt*, then youll have a certificate for ``mail.your.doma.in`` now. However,
``mail.your.doma.in`` might only be the location where you want the Mailu web-interfaces to live — your mail should be sent/received from ``your.doma.in``,
and this is the ``DOMAIN`` in your ``.env``?
To support that use-case, Traefik can request ``SANs`` for your domain. Lets add something like
.. code-block:: guess
[acme]
[[acme.domains]]
main = "your.doma.in" # this is the same as $TRAEFIK_DOMAIN!
sans = ["mail.your.doma.in", "webmail.your.doma.in", "smtp.your.doma.in"]
to your ``traefik.toml``. You might need to clear your ``acme.json``, if a certificate for one of these domains already exists.
You will need some solution which dumps the certificates in ``acme.json``, so you can include them in the ``mailu/front`` container.
One such example is ``mailu/traefik-certdumper``, which has been adapted for use in Mailu. You can add it to your ``docker-compose.yml`` like:
.. code-block:: yaml
certdumper:
restart: always
image: mailu/traefik-certdumper:$VERSION
environment:
# Make sure this is the same as the main=-domain in traefik.toml
# !!! Also dont forget to add "TRAEFIK_DOMAIN=[...]" to your .env!
- DOMAIN=$TRAEFIK_DOMAIN
volumes:
- "/data/traefik:/traefik"
- "$ROOT/certs:/output"
Assuming you have ``volume-mounted`` your ``acme.json`` put to ``/data/traefik`` on your host. The dumper will then write out ``/data/traefik/ssl/your.doma.in.crt``
and ``/data/traefik/ssl/your.doma.in.key`` whenever ``acme.json`` is updated. Yay! Now lets mount this to our ``front`` container like:
.. code-block:: yaml
volumes:
- "$ROOT/overrides/nginx:/overrides"
- /data/traefik/ssl/$TRAEFIK_DOMAIN.crt:/certs/cert.pem
- /data/traefik/ssl/$TRAEFIK_DOMAIN.key:/certs/key.pem
.. _`Traefik`: https://traefik.io/
Override Mailu configuration Override Mailu configuration
---------------------------- ----------------------------
If you do not have the resources for running a separate reverse proxy, you could override Mailu reverse proxy configuration by using a Docker volume. Simply store your configuration file (Nginx format), in ``/mailu/nginx.conf`` for instance. If you do not have the resources for running a separate reverse proxy, you could override Mailu reverse proxy configuration by using a Docker volume.
Simply store your configuration file (Nginx format), in ``/mailu/nginx.conf`` for instance.
Then modify your ``docker-compose.yml`` file and change the ``front`` section to add a mount: Then modify your ``docker-compose.yml`` file and change the ``front`` section to add a mount:
@ -135,7 +212,10 @@ Then modify your ``docker-compose.yml`` file and change the ``front`` section to
- "$ROOT/certs:/certs" - "$ROOT/certs:/certs"
- "$ROOT/nginx.conf:/etc/nginx/nginx.conf" - "$ROOT/nginx.conf:/etc/nginx/nginx.conf"
You can use our default configuration file as a sane base for your configuration. You can also download the example configuration files:
- :download:`compose/traefik/docker-compose.yml`
- :download:`compose/traefik/traefik.toml`
Disable completely Mailu reverse proxy Disable completely Mailu reverse proxy
-------------------------------------- --------------------------------------

@ -0,0 +1,11 @@
FROM alpine:3.8
RUN apk --no-cache add inotify-tools jq openssl util-linux bash docker
# while not strictly documented, this script seems to always(?) support previous acme.json versions too
RUN wget https://raw.githubusercontent.com/containous/traefik/master/contrib/scripts/dumpcerts.sh -O dumpcerts.sh
VOLUME ["/traefik"]
VOLUME ["/output"]
COPY run.sh /
ENTRYPOINT ["/run.sh"]

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Sven Dowideit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,27 @@
# Single-domain traefik-certdumper for mailu
This is based on the work by Sven Dowideit on https://github.com/SvenDowideit/traefik-certdumper
## Fork?
This is a slight modification that is less flexible, but is adapted to the
usecase in mailu. If you wish to deploy mailu behind a traefik, you face many
problems. One of these is that you need to get the certificates into mailu in a
very defined manner. This will copy the certificate for the **Main:**-domain
given in the DOMAIN-environment onto `output`.
If your output happens to be mailu-front-`/certs`, the certificate-watcher in
the front-container will catch it and reload nginx. This works for mailu
`TLS_FLAVOR=[mail, cert]`
```
certdumper:
restart: always
image: Mailu/traefik-certdumper:$VERSION
environment:
- DOMAIN=$DOMAIN
volumes:
# your traefik data-volume is probably declared outside of the mailu composefile
- /data/traefik:/traefik
- $ROOT/certs/:/output/
```

@ -0,0 +1,30 @@
#!/bin/bash
function dump() {
echo "$(date) Dumping certificates"
bash dumpcerts.sh /traefik/acme.json /tmp/work/ || return
for crt_file in $(ls /tmp/work/certs/*); do
pem_file=$(echo $crt_file | sed 's/certs/pem/g' | sed 's/.crt/-public.pem/g')
echo "openssl x509 -inform PEM -in $crt_file > $pem_file"
openssl x509 -inform PEM -in $crt_file > $pem_file
done
for key_file in $(ls /tmp/work/private/*); do
pem_file=$(echo $key_file | sed 's/private/pem/g' | sed 's/.key/-private.pem/g')
echo "openssl rsa -in $key_file -text > $pem_file"
openssl rsa -in $key_file -text > $pem_file
done
echo "$(date) Copying certificates"
cp -v /tmp/work/pem/${DOMAIN}-private.pem /output/key.pem
cp -v /tmp/work/pem/${DOMAIN}-public.pem /output/cert.pem
}
mkdir -p /tmp/work/pem /tmp/work/certs
# run once on start to make sure we have any old certs
dump
while true; do
inotifywait -e modify /traefik/acme.json && \
dump
done

@ -30,6 +30,10 @@ services:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}radicale:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}radicale:${MAILU_VERSION:-local}
build: ../optional/radicale build: ../optional/radicale
traefik-certdumper:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}traefik-certdumper:${MAILU_VERSION:-local}
build: ../optional/traefik-certdumper
admin: admin:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}admin:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}admin:${MAILU_VERSION:-local}
build: ../core/admin build: ../core/admin

Loading…
Cancel
Save