Use server-side password generator for generating token.

Fix setup correctly writing the value for API to mailu.env
Normalize env vars for front container.
Update reverse proxy with API information.
main
Dimitri Huisman 2 years ago
parent 0673d32306
commit 75afe1092d
No known key found for this signature in database

@ -252,7 +252,7 @@ http {
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if API == 'true' %} {% if API %}
location ~ {{ WEB_API or '/api' }} { location ~ {{ WEB_API or '/api' }} {
include /etc/nginx/proxy.conf; include /etc/nginx/proxy.conf;
proxy_pass http://$admin; proxy_pass http://$admin;

@ -5,8 +5,7 @@ import logging as log
import sys import sys
from socrate import system, conf from socrate import system, conf
system.set_env() args = system.set_env()
args = os.environ.copy()
log.basicConfig(stream=sys.stderr, level=args.get("LOG_LEVEL", "WARNING")) log.basicConfig(stream=sys.stderr, level=args.get("LOG_LEVEL", "WARNING"))
args['TLS_PERMISSIVE'] = str(args.get('TLS_PERMISSIVE')).lower() not in ('false', 'no') args['TLS_PERMISSIVE'] = str(args.get('TLS_PERMISSIVE')).lower() not in ('false', 'no')

@ -1,17 +1,17 @@
Using an external reverse proxy Using an external reverse proxy
=============================== ===============================
One of Mailu's use cases is as part of a larger services platform, where maybe One of Mailu's use cases is as part of a larger services platform, where maybe
other Web services are available than just Mailu Webmail and Admin interfaces. other Web services are available than just Mailu Webmail and Admin interfaces.
In such a configuration, one would usually run a frontend reverse proxy to serve all In such a configuration, one would usually run a frontend reverse proxy to serve all
Web contents based on criteria like the requested hostname (virtual hosts) Web contents based on criteria like the requested hostname (virtual hosts)
and/or the requested path. and/or the requested path.
The Mailu Admin Web frontend is disabled in the default setup for security reasons, The Mailu Admin Web frontend is disabled in the default setup for security reasons,
it is however expected that most users will enable it at some point. Also, due it is however expected that most users will enable it at some point. Also, due
to the Docker Compose configuration structure, it is impossible for us to facilitate to the Docker Compose configuration structure, it is impossible for us to facilitate
disabling the Web frontend with a configuration variable. This guide was written to disabling the Web frontend with a configuration variable. This guide was written to
help users setup such an architecture. help users setup such an architecture.
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:
@ -22,13 +22,13 @@ There are basically three options, from the most to the least recommended one:
All options will require that you modify the ``docker-compose.yml`` and ``mailu.env`` 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. 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. 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
-------------------------------------- --------------------------------------
The simplest and safest option is to modify the port forwards for Mailu Web frontend and have your own frontend point there. The simplest and safest option is to modify the port forwards for Mailu Web frontend and have your own frontend point there.
For instance, in the ``front`` section of Mailu ``docker-compose.yml``, use local ports 8080 and 8443 respectively for HTTP and HTTPS: For instance, in the ``front`` section of Mailu ``docker-compose.yml``, use local ports 8080 and 8443 respectively for HTTP and HTTPS:
.. code-block:: yaml .. code-block:: yaml
@ -45,7 +45,7 @@ For instance, in the ``front`` section of Mailu ``docker-compose.yml``, use loca
volumes: volumes:
- "$ROOT/certs:/certs" - "$ROOT/certs:/certs"
Then on your own frontend, point to these local ports. In practice, you only need to point to the HTTPS port Then on your own frontend, point to these local ports. In practice, you only need to point to the HTTPS port
(as the HTTP port simply redirects there). Here is an example Nginx configuration: (as the HTTP port simply redirects there). Here is an example Nginx configuration:
.. code-block:: nginx .. code-block:: nginx
@ -68,19 +68,19 @@ Then on your own frontend, point to these local ports. In practice, you only nee
#mailu.env file #mailu.env file
REAL_IP_HEADER=X-Real-IP REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y 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. #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``, the client-autoconfiguration 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 RESTful API as ``/api``, the Webmail as ``/webmail``, the single sign on page as ``/sso``, webdav as ``/webdav``, the client-autoconfiguration 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
server { server {
# [...] here goes your standard configuration # [...] here goes your standard configuration
location ~* ^/(admin|sso|static|webdav|webmail|(apple\.)?mobileconfig|(\.well\-known/autoconfig/)?mail/|Autodiscover/Autodiscover) { location ~* ^/(admin|api|sso|static|webdav|webmail|(apple\.)?mobileconfig|(\.well\-known/autoconfig/)?mail/|Autodiscover/Autodiscover) {
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_pass https://localhost:8443; proxy_pass https://localhost:8443;
} }
location /main_app { location /main_app {
@ -103,13 +103,13 @@ Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``
.. note:: Please dont add a ``/`` at the end of the location pattern or all your redirects will fail with 404 because the ``/`` would be missing, and you would have to add it manually to move on .. note:: Please dont add a ``/`` at the end of the location pattern or all your redirects will fail with 404 because the ``/`` would be missing, and you would have to add it manually to move on
.. code-block:: docker .. code-block:: docker
#mailu.env file #mailu.env file
REAL_IP_HEADER=X-Real-IP REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y 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. #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 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). 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 :
@ -147,7 +147,7 @@ Here is an example configuration :
#mailu.env file #mailu.env file
REAL_IP_HEADER=X-Real-IP REAL_IP_HEADER=X-Real-IP
REAL_IP_FROM=x.x.x.x,y.y.y.y.y 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. #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:
@ -166,8 +166,8 @@ Traefik as reverse proxy
As such, many may wish to integrate Mailu into a system which already uses Traefik as its sole ingress/reverse-proxy. 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 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 , 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. forward non-HTTP, nor can it easily forward HTTPS-to-HTTPS.
This, however, means 3 things: This, however, means 3 things:
@ -175,9 +175,9 @@ This, however, means 3 things:
- ``mailu/front`` is not exposed to the outside world on ``HTTP`` - ``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 - ``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 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``. 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 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. 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``, To set this up, first set ``TLS_FLAVOR=mail`` in your ``.env``. This tells ``mailu/front`` not to try to request certificates using ``letsencrypt``,
@ -194,20 +194,20 @@ Add the respective Traefik labels for your domain/configuration, like
.. note:: Please dont forget to add ``TRAEFIK_DOMAIN=[...]`` TO YOUR ``.env`` .. 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 If your Traefik is configured to automatically request certificates from *letsencrypt*, then youll have a certificate
for ``mail.your.example.com`` now. However, ``mail.your.example.com`` might only be the location where you want the Mailu web-interfaces for ``mail.your.example.com`` now. However, ``mail.your.example.com`` might only be the location where you want the Mailu web-interfaces
to live — your mail should be sent/received from ``your.example.com``, and this is the ``DOMAIN`` in your ``.env``? to live — your mail should be sent/received from ``your.example.com``, 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 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: client IP. This is configured in mailu.env:
.. code-block:: docker .. code-block:: docker
#mailu.env file #mailu.env file
REAL_IP_HEADER=X-Real-Ip REAL_IP_HEADER=X-Real-Ip
REAL_IP_FROM=x.x.x.x,y.y.y.y.y 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. #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. For more information see the :ref:`configuration reference <reverse_proxy_headers>` for more information.
@ -235,7 +235,7 @@ Add the appropriate labels for your domain(s) to the ``front`` container in ``do
Of course, be sure to define the Certificate Resolver ``foo`` in the static configuration as well. Of course, be sure to define the Certificate Resolver ``foo`` in the static configuration as well.
Alternatively, you can define SANs in the Traefik static configuration using routers, or in the static configuration using entrypoints. 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. Refer to the Traefik documentation for more details.
.. _`Traefik`: https://traefik.io/ .. _`Traefik`: https://traefik.io/

@ -1,7 +1,5 @@
//API_TOKEN generator //Store API token in variable.
var random_array = new Uint32Array(2); var token = $("#api_token").val();
crypto.getRandomValues(random_array);
var token = random_array[0].toString() + random_array[1].toString();
$(document).ready(function() { $(document).ready(function() {
if ($("#webmail").val() == 'none') { if ($("#webmail").val() == 'none') {
@ -39,7 +37,7 @@ $(document).ready(function() {
}); });
$(document).ready(function() { $(document).ready(function() {
if ($('#api').prop('checked')) { if ($('#api_enabled').prop('checked')) {
$("#api_path").show(); $("#api_path").show();
$("#api_path").val("/api") $("#api_path").val("/api")
$("#api_token").show(); $("#api_token").show();
@ -54,7 +52,7 @@ $(document).ready(function() {
$("#api_token").val(""); $("#api_token").val("");
$("#api_token_label").hide(); $("#api_token_label").hide();
} }
$("#api").change(function() { $("#api_enabled").change(function() {
if ($(this).is(":checked")) { if ($(this).is(":checked")) {
$("#api_path").show(); $("#api_path").show();
$("#api_path").val("/api"); $("#api_path").val("/api");

@ -93,11 +93,11 @@ manage your email domains, users, etc.</p>
It is not possible to use the API without an API token.</p> It is not possible to use the API without an API token.</p>
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="api_enabled" value="true" id="api" > <input type="checkbox" name="api_enabled" value="true" id="api_enabled" >
<label>Enable the API (and path to the API)</label> <label>Enable the API (and path to the API)</label>
<input class="form-control" type="text" name="api_path" id="api_path" style="display: none"> <input class="form-control" type="text" name="api_path" id="api_path" style="display: none">
<label name="api_token_label" id="api_token_label">API token</label> <label name="api_token_label" id="api_token_label">API token</label>
<input class="form-control" type="text" name="api_token" id="api_token" style="display: none"> <input class="form-control" type="text" name="api_token" id="api_token" style="display: none" value="{{ secret(32) }}">
</div> </div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

Loading…
Cancel
Save