diff --git a/core/nginx/config.py b/core/nginx/config.py index cee8bce4..94bb26b0 100755 --- a/core/nginx/config.py +++ b/core/nginx/config.py @@ -5,8 +5,7 @@ import logging as log import sys from socrate import system, conf -system.set_env() -args = os.environ.copy() +args = system.set_env() log.basicConfig(stream=sys.stderr, level=args.get("LOG_LEVEL", "WARNING")) args['TLS_PERMISSIVE'] = str(args.get('TLS_PERMISSIVE')).lower() not in ('false', 'no') @@ -18,8 +17,8 @@ with open("/etc/resolv.conf") as handle: args["RESOLVER"] = f"[{resolver}]" if ":" in resolver else resolver # TLS configuration -cert_name = os.getenv("TLS_CERT_FILENAME", default="cert.pem") -keypair_name = os.getenv("TLS_KEYPAIR_FILENAME", default="key.pem") +cert_name = args.get("TLS_CERT_FILENAME", "cert.pem") +keypair_name = args.get("TLS_KEYPAIR_FILENAME", "key.pem") args["TLS"] = { "cert": ("/certs/%s" % cert_name, "/certs/%s" % keypair_name), "letsencrypt": ("/certs/letsencrypt/live/mailu/nginx-chain.pem", @@ -37,7 +36,7 @@ def format_for_nginx(fullchain, output): split = '-----END CERTIFICATE-----\n' with open(fullchain, 'r') as pem: certs = [f'{cert}{split}' for cert in pem.read().split(split) if cert] - if len(certs)>2 and os.getenv('LETSENCRYPT_SHORTCHAIN'): + if len(certs)>2 and args.get('LETSENCRYPT_SHORTCHAIN'): del certs[-1] with open(output, 'w') as pem: pem.write(''.join(certs)) diff --git a/docs/reverse.rst b/docs/reverse.rst index a5a12bfb..2fec15f7 100644 --- a/docs/reverse.rst +++ b/docs/reverse.rst @@ -1,17 +1,17 @@ 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. -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) -and/or the requested path. +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) +and/or the requested path. -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 -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 +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 +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 help users setup such an architecture. 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. -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 ` for more information. 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: .. code-block:: yaml @@ -45,7 +45,7 @@ For instance, in the ``front`` section of Mailu ``docker-compose.yml``, use loca volumes: - "$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: .. 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 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``, 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): + #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 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 server { # [...] 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 X-Real-IP $remote_addr; - proxy_pass https://localhost:8443; + proxy_pass https://localhost:8443; } location /main_app { @@ -103,13 +103,13 @@ Because the admin interface is served as ``/admin``, the Webmail as ``/webmail`` .. note:: Please don’t 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 - + #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. + #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). Here is an example configuration : @@ -147,7 +147,7 @@ Here is an example configuration : #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. + #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: @@ -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 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. +, 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: @@ -175,9 +175,9 @@ This, however, means 3 things: - ``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 +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``, @@ -194,20 +194,20 @@ Add the respective Traefik labels for your domain/configuration, like .. note:: Please don’t forget to add ``TRAEFIK_DOMAIN=[...]`` TO YOUR ``.env`` -If your Traefik is configured to automatically request certificates from *letsencrypt*, then you’ll 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 +If your Traefik is configured to automatically request certificates from *letsencrypt*, then you’ll 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 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. -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: .. 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. + #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 ` 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. -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. .. _`Traefik`: https://traefik.io/ diff --git a/setup/static/render.js b/setup/static/render.js index b2cdc7c8..2a1d0fae 100644 --- a/setup/static/render.js +++ b/setup/static/render.js @@ -1,12 +1,5 @@ -//API_TOKEN generator -var chars = "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -var tokenLength = 12; -var token = ""; - -for (var i = 0; i <= tokenLength; i++) { - var randomNumber = Math.floor(Math.random() * chars.length); - token += chars.substring(randomNumber, randomNumber +1); - } +//Store API token in variable. +var token = $("#api_token").val(); $(document).ready(function() { if ($("#webmail").val() == 'none') { @@ -44,7 +37,7 @@ $(document).ready(function() { }); $(document).ready(function() { - if ($('#api').prop('checked')) { + if ($('#api_enabled').prop('checked')) { $("#api_path").show(); $("#api_path").val("/api") $("#api_token").show(); @@ -53,13 +46,13 @@ $(document).ready(function() { $("#api_token_label").show(); } else { $("#api_path").hide(); - $("#api_path").val("/api") + $("#api_path").val("") $("#api_token").hide(); $("#api_token").prop('required',false); $("#api_token").val(""); $("#api_token_label").hide(); } - $("#api").change(function() { + $("#api_enabled").change(function() { if ($(this).is(":checked")) { $("#api_path").show(); $("#api_path").val("/api"); @@ -69,7 +62,7 @@ $(document).ready(function() { $("#api_token_label").show(); } else { $("#api_path").hide(); - $("#api_path").val("/api") + $("#api_path").val("") $("#api_token").hide(); $("#api_token").prop('required',false); $("#api_token").val(""); diff --git a/setup/templates/steps/config.html b/setup/templates/steps/config.html index 19736448..fe8bc0cb 100644 --- a/setup/templates/steps/config.html +++ b/setup/templates/steps/config.html @@ -93,11 +93,11 @@ manage your email domains, users, etc.

It is not possible to use the API without an API token.

- + - +