2052: Update reverse proxy documentation (see #1962). r=mergify[bot] a=Diman0
## What type of PR?
Bug-fix / documentation
## What does this PR do?
PR #1959 introduces functionality that Mailu must be told what header to trust from a reverse proxy. This PR updates the documentation that for a reverse proxy a header must be configured for passing the remote client IP.
And that in mailu.env file you must configure what header is used by the reverse proxy and what the IP address is of this reverse proxy.
### Related issue(s)
- Auto close an issue like: closes#1962
## Prerequisites
Before we can consider review and merge, please make sure the following list is done and checked.
If an entry in not applicable, you can check it or remove it from the list.
- [x] In case of feature or enhancement: documentation updated accordingly
- [x] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file.
Co-authored-by: Dimitri Huisman <diman@huisman.xyz>
@ -194,7 +194,9 @@ The ``LETSENCRYPT_SHORTCHAIN`` (default: False) setting controls whether we send
.._`android handsets older than 7.1.1`: https://community.letsencrypt.org/t/production-chain-changes/150739
.._`android handsets older than 7.1.1`: https://community.letsencrypt.org/t/production-chain-changes/150739
The ``REAL_IP_HEADER`` (default: unset) and ``REAL_IP_FROM`` (default: unset) settings controls whether HTTP headers such as ``X-Forwarded-For`` or ``X-Real-IP`` should be trusted. The former should be the name of the HTTP header to extract the client IP address from and the later a comma separated list of IP addresses designing which proxies to trust. If you are using Mailu behind a reverse proxy, you should set both. Setting the former without the later introduces a security vulnerability allowing a potential attacker to spoof his source address.
.._reverse_proxy_headers:
The ``REAL_IP_HEADER`` (default: unset) and ``REAL_IP_FROM`` (default: unset) settings controls whether HTTP headers such as ``X-Forwarded-For`` or ``X-Real-IP`` should be trusted. The former should be the name of the HTTP header to extract the client IP address from and the later a comma separated list of IP addresses designating which proxies to trust. If you are using Mailu behind a reverse proxy, you should set both. Setting the former without the later introduces a security vulnerability allowing a potential attacker to spoof his source address.
The ``TZ`` sets the timezone Mailu will use. The timezone naming convention usually uses a ``Region/City`` format. See `TZ database name`_ for a list of valid timezones This defaults to ``Etc/UTC``. Warning: if you are observing different timestamps in your log files you should change your hosts timezone to UTC instead of changing TZ to your local timezone. Using UTC allows easy log correlation with remote MTAs.
The ``TZ`` sets the timezone Mailu will use. The timezone naming convention usually uses a ``Region/City`` format. See `TZ database name`_ for a list of valid timezones This defaults to ``Etc/UTC``. Warning: if you are observing different timestamps in your log files you should change your hosts timezone to UTC instead of changing TZ to your local timezone. Using UTC allows easy log correlation with remote MTAs.
@ -11,7 +11,10 @@ There are basically three options, from the most to the least recommended one:
- `use Traefik in another container as central system-reverse-proxy`_
- `use Traefik in another container as central system-reverse-proxy`_
- `override Mailu Web frontend configuration`_
- `override Mailu Web frontend configuration`_
All options will require that you modify the ``docker-compose.yml`` file.
All options will require that you modify the ``docker-compose.yml`` and ``mailu.env`` file.
Mailu must also be configured with the information what header is used by the reverse proxy for passing the remote client IP.
This is configured in the mailu.env file. See the :ref:`configuration reference <reverse_proxy_headers>` for more information.
Have Mailu Web frontend listen locally
Have Mailu Web frontend listen locally
--------------------------------------
--------------------------------------
@ -43,10 +46,19 @@ Then on your own frontend, point to these local ports. In practice, you only nee
# [...] here goes your standard configuration
# [...] here goes your standard configuration
location / {
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr
proxy_pass https://localhost:8443;
proxy_pass https://localhost:8443;
}
}
}
}
..code-block:: docker
#mailu.env file
REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y
#x.x.x.x,y.y.y.y.y is the static IP address your reverse proxy uses for connecting to Mailu.
Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``, the single sign on page as ``/sso``, webdav as ``/webdav`` and the static files endpoint as ``/static``, you may also want to use a single virtual host and serve other applications (still Nginx):
Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``, the single sign on page as ``/sso``, webdav as ``/webdav`` and the static files endpoint as ``/static``, you may also want to use a single virtual host and serve other applications (still Nginx):
..code-block:: nginx
..code-block:: nginx
@ -55,8 +67,9 @@ Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``
# [...] here goes your standard configuration
# [...] here goes your standard configuration
location ~ ^/(admin|sso|static|webdav|webmail)/ {
location ~ ^/(admin|sso|static|webdav|webmail)/ {
proxy_pass https://localhost:8443;
proxy_set_header Host $host;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr
proxy_pass https://localhost:8443;
}
}
location /main_app {
location /main_app {
@ -76,6 +89,13 @@ Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``
}
}
}
}
..code-block:: docker
#mailu.env file
REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y
#x.x.x.x,y.y.y.y.y is the static IP address your reverse proxy uses for connecting to Mailu.
Finally, you might want to serve the admin interface on a separate virtual host but not expose the admin container directly (have your own HTTPS virtual hosts on top of Mailu, one public for the Webmail and one internal for administration for instance).
Finally, you might want to serve the admin interface on a separate virtual host but not expose the admin container directly (have your own HTTPS virtual hosts on top of Mailu, one public for the Webmail and one internal for administration for instance).
Here is an example configuration :
Here is an example configuration :
@ -88,6 +108,8 @@ Here is an example configuration :
# [...] here goes your standard configuration
# [...] here goes your standard configuration
location /webmail {
location /webmail {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr
proxy_pass https://localhost:8443/webmail;
proxy_pass https://localhost:8443/webmail;
}
}
}
}
@ -98,12 +120,21 @@ Here is an example configuration :
# [...] here goes your standard configuration
# [...] here goes your standard configuration
location /admin {
location /admin {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr
proxy_pass https://localhost:8443/admin;
proxy_pass https://localhost:8443/admin;
proxy_set_header Host $http_host;
proxy_set_header Host $http_host;
}
}
}
}
..code-block:: docker
#mailu.env file
REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y
#x.x.x.x,y.y.y.y.y is the static IP address your reverse proxy uses for connecting to Mailu.
Depending on how you access the front server, you might want to add a ``proxy_redirect`` directive to your ``location`` blocks:
Depending on how you access the front server, you might want to add a ``proxy_redirect`` directive to your ``location`` blocks:
..code-block:: nginx
..code-block:: nginx
@ -151,7 +182,16 @@ If your Traefik is configured to automatically request certificates from *letsen
and this is the ``DOMAIN`` in your ``.env``?
and this is the ``DOMAIN`` in your ``.env``?
To support that use-case, Traefik can request ``SANs`` for your domain. The configuration for this will depend on your Traefik version.
To support that use-case, Traefik can request ``SANs`` for your domain. The configuration for this will depend on your Traefik version.
----
Mailu must also be configured with the information what header is used by the reverse proxy for passing the remote client IP. This is configured in mailu.env:
..code-block:: docker
#mailu.env file
REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y
#x.x.x.x,y.y.y.y.y is the static IP address your reverse proxy uses for connecting to Mailu.
For more information see the :ref:`configuration reference <reverse_proxy_headers>` for more information.
Traefik 2.x using labels configuration
Traefik 2.x using labels configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -179,53 +219,6 @@ Of course, be sure to define the Certificate Resolver ``foo`` in the static conf
Alternatively, you can define SANs in the Traefik static configuration using routers, or in the static configuration using entrypoints. Refer to the Traefik documentation for more details.
Alternatively, you can define SANs in the Traefik static configuration using routers, or in the static configuration using entrypoints. Refer to the Traefik documentation for more details.
Traefik 1.x with TOML configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Lets add something like
..code-block:: yaml
[acme]
[[acme.domains]]
main = "your.example.com" # this is the same as $TRAEFIK_DOMAIN!
sans = ["mail.your.example.com", "webmail.your.example.com", "smtp.your.example.com"]
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 don’t forget to add "TRAEFIK_DOMAIN=[...]" to your .env!
- DOMAIN=$TRAEFIK_DOMAIN
volumes:
# Folder, which contains the acme.json
- "/data/traefik:/traefik"
# Folder, where cert.pem and key.pem will be written
- "/data/mailu/certs:/output"
Assuming you have ``volume-mounted`` your ``acme.json`` put to ``/data/traefik`` on your host. The dumper will then write out ``/data/mailu/certs/cert.pem`` and ``/data/mailu/certs/key.pem`` whenever ``acme.json`` is updated.
Yay! Now let’s mount this to our ``front`` container like:
..code-block:: yaml
volumes:
- /data/mailu/certs:/certs
This works, because we set ``TLS_FLAVOR=mail``, which picks up the key-certificate pair (e.g., ``cert.pem`` and ``key.pem``) from the certs folder in the root path (``/certs/``).