Merge branch 'master' into feat-switch-buildx

master
Dimitri Huisman 2 years ago
commit 3aafecafe7

@ -11,7 +11,7 @@ RUN apk add --no-cache \
python3 py3-pip git bash py3-multidict py3-yarl tzdata \
&& pip3 install --upgrade pip
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, snappymail, roundcube
RUN pip3 install socrate==0.2.0
# Shared layer between dovecot and postfix

@ -11,7 +11,7 @@ RUN apk add --no-cache \
python3 py3-pip git bash py3-multidict \
&& pip3 install --upgrade pip
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, snappymail, roundcube
RUN pip3 install socrate==0.2.0
# Image specific layers under this line

@ -12,7 +12,7 @@ RUN apk add --no-cache \
python3 py3-pip git bash py3-multidict py3-yarl tzdata \
&& pip3 install --upgrade pip
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, snappymail, roundcube
RUN pip3 install socrate==0.2.0
# Shared layer between dovecot and postfix

@ -10,7 +10,7 @@ RUN apk add --no-cache \
python3 py3-pip git bash py3-multidict tzdata \
&& pip3 install --upgrade pip
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, snappymail, roundcube
RUN pip3 install socrate==0.2.0
# Image specific layers under this line

@ -39,16 +39,16 @@ Postfix configuration overrides.
RSpamD configuration overrides.
#### Rainloop
#### Snappymail
- Old path: `/mailu/webmail/_data_/_default_/storage` (part of `/mailu/webmail` mountpoint, shared with Roundcube)
- New path: `/mailu/config/rainloop`
- New path: `/mailu/config/snappymail`
User specific configs. The remaining files under the old `/mailu/webmail` don't need to be persistent. Except for `AddressBook.sqlite`, see `/mailu/data`.
#### Roundcube
- Old path: `/mailu/webmail/gpg` (part of `/mailu/webmail` mountpoint, shared with Rainloop)
- Old path: `/mailu/webmail/gpg` (part of `/mailu/webmail` mountpoint, shared with Snappymail)
- New path: `/mailu/config/roundcube/gpg`
User configured GPG keys.
@ -108,10 +108,10 @@ This move is needed in order to be able to mount the directory without exposing
Storage of Bayes and Fuzzy learning SQLite databases and caches. As future optimization we should look into moving all this into Redis.
#### Rainloop
#### SnappyMail
- Old path: `/mailu/webmail/_data_/_default_/AddressBook.sqlite` (part of `/mailu/webmail` mountpoint, shared with Roundcube)
- New path: `/mailu/data/rainloop/AddressBook.sqlite` (mount on `rainloop` directory)
- New path: `/mailu/data/snappymail/AddressBook.sqlite` (mount on `snappymail` directory)
Addressbook SQLite file. For future replicated deployments this might better be configured to use an external DB.
@ -119,7 +119,7 @@ For this modification, the `AddressBook.sqlite` will need to be moved to a diffe
#### Roundcube
- Old path: `/mailu/webmail/roundcube.db` (part of `/mailu/webmail` mountpoint, shared with Rainloop)
- Old path: `/mailu/webmail/roundcube.db` (part of `/mailu/webmail` mountpoint, shared with SnappyMail)
- New path: `/mailu/data/roundcube/roundcube.db` (mount on `roundcube` directory)
User settings SQLite database file for roundcube. For future replicated deployments this might better be configured to use an external DB.
@ -163,7 +163,7 @@ The final layout of the Mailu filesystem will look like:
├── config
│   ├── dovecot
│   ├── postfix
│   ├── rainloop
│   ├── snappymail
│   ├── redis
│   ├── roundcube
│   │   └── gpg
@ -173,7 +173,7 @@ The final layout of the Mailu filesystem will look like:
│   └── dkim
├── data
│   ├── admin
│   ├── rainloop
│   ├── snappymail
│   ├── roundcube
│   └── rspamd
├── local

@ -8,7 +8,7 @@ How does spam filtering work in Mailu?
Mailu uses Rspamd for spam filtering. Rspamd is a Fast, free and open-source spam filtering system.
Rspamd rejects non-compliant email messages and email messages that contain virusses. In Mailu Rspamd uses a scoring scale from 0 to 15. The following values are the default values, and can be changed inside the Rspamd webgui under the tab configuration:
Rspamd rejects non-compliant email messages and email messages that contain virusses. In Mailu Rspamd uses a scoring scale from 0 to 15. The following values are the default values, and can be changed inside the Rspamd WebUI under the tab configuration:
* Email messages with a score of 15 or higher will be rejected.
@ -52,7 +52,7 @@ The location in the administration web interface where the spam filter and spam
Can I learn ham/spam messages from an already existing mailbox?
---------------------------------------------------------------
Mailu supports automatic spam learning for email messages moved to the Junk Folder. Any email messages moved to the Junk Folder will be re-learned as spam, any email moved from the Junk Folder to any other folder (but the thrash folder) will be re-learned as ham.
Mailu supports automatic spam learning for email messages moved to the Junk Folder. Any email messages moved to the Junk Folder will be re-learned as spam, any email moved from the Junk Folder to any other folder (but the trash folder) will be re-learned as ham.
If you already have an existing mailbox and want Mailu to learn them all as ham messages, you might run rspamc from within the dovecot container:

@ -50,7 +50,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=false
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)

@ -23,16 +23,16 @@ the latest Linux kernel. The minimal required memory and swap are:
Pick a distribution
-------------------
The mail server runs as a set of Docker containers. It is thus almost agnostic
of the underlying operating system as long as a fairly recent Linux kernel is
running and the Docker API (>= 1.11) is available.
The mail server runs as a set of Docker containers, so it is almost operating
system agnostic as long as a fairly recent Linux kernel is running and
the Docker API (>= 1.11) is available.
Because most of our tests run on Debian Jessie and Debian Stretch, we recommend
one of these for the base system. Mailu should however be able to run on
any of the `officially supported distributions`_.
For the purpose of this guide, all examples are based on Debian Stretch. The
differences with other system will hardly be noticeable however.
differences with other system will however hardly be noticeable.
.. _`officially supported distributions`: https://docs.docker.com/engine/installation/
@ -59,10 +59,9 @@ Mailu uses Docker port forwarding from the host to make services
available to external users. First, your host should have a public IP address
configured (see ``/etc/network/interfaces``) or your router should
forward connections to its internal IP address. Due to spam problems and
reputation services, it
is highly recommended that you use a dedicated IP address for your mail server
and that you have a dedicated hostname with forward and reverse DNS entries
for this IP address.
reputation services, it is highly recommended that you use a dedicated IP
address for your mail server and that you have a dedicated hostname
with forward and reverse DNS entries for this IP address.
Also, your host must not listen on ports ``25``, ``80``, ``110``, ``143``,
``443``, ``465``, ``587``, ``993`` or ``995`` as these are used by Mailu

@ -65,7 +65,8 @@ You can find those addresses by running the following:
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
If the address is not configured directly (NAT) on any of the network interfaces or if
you would simply like the server to listen on all interfaces, use ``0.0.0.0`` and ``::``. Note that running in this mode is not supported and can lead to `issues`_.
you would simply like the server to listen on all interfaces, use ``0.0.0.0`` and ``::``.
Note that running in this mode is not supported and can lead to `issues`_.
.. _issues: https://github.com/Mailu/Mailu/issues/641
@ -107,6 +108,7 @@ Else, if you don't go with the automatic way, you need to manually create the ad
docker-compose exec admin flask mailu admin me example.net 'password'
This will create a user named ``me@example.net`` with password ``password`` and administration privileges. Connect to the Web admin interface and change the password to a strong one.
This will create a user named ``me@example.net`` with password ``password`` and administration privileges.
Connect to the Web admin interface and change the password to a strong one.
.. note:: It is vitally important that either a user with the same email as ``POSTMASTER`` in your ``mailu.env`` exists, or you remember to create an alias with this name after you log in. All kinds of strange errors will occur as a result of not doing so!

@ -37,7 +37,8 @@ The ``POSTMASTER`` is the local part of the postmaster email address. It is
recommended to setup a generic value and later configure a mail alias for that
address.
The ``WILDCARD_SENDERS`` setting is a comma delimited list of user email addresses that are allowed to send emails from any existing address (spoofing the sender).
The ``WILDCARD_SENDERS`` setting is a comma delimited list of user email addresses
that are allowed to send emails from any existing address (spoofing the sender).
The ``AUTH_RATELIMIT_IP`` (default: 60/hour) holds a security setting for fighting
attackers that waste server resources by trying to guess user passwords (typically
@ -60,7 +61,7 @@ CIDRs that won't be subject to any form of rate limiting. Specifying ``0.0.0.0/0
there is a good way to disable rate limiting altogether.
The ``TLS_FLAVOR`` sets how Mailu handles TLS connections. Setting this value to
``notls`` will cause Mailu not to server any web content! More on :ref:`tls_flavor`.
``notls`` will cause Mailu not to serve any web content! More on :ref:`tls_flavor`.
Mail settings
-------------
@ -129,8 +130,10 @@ Web settings
- ``WEB_WEBMAIL`` contains the path to the Web email client.
- ``WEBROOT_REDIRECT`` redirects all non-found queries to the set path.
An empty ``WEBROOT_REDIRECT`` value disables redirecting and enables classic behavior of a 404 result when not found.
Alternatively, ``WEBROOT_REDIRECT`` can be set to ``none`` if you are using an Nginx override for ``location /``.
An empty ``WEBROOT_REDIRECT`` value disables redirecting and enables
classic behavior of a 404 result when not found.
Alternatively, ``WEBROOT_REDIRECT`` can be set to ``none`` if you
are using an Nginx override for ``location /``.
All three options need a leading slash (``/``) to work.
@ -142,10 +145,12 @@ Both ``SITENAME`` and ``WEBSITE`` are customization options for the panel menu
in the admin interface, while ``SITENAME`` is a customization option for
every Web interface.
- ``LOGO_BACKGROUND`` sets a custom background colour for the brand logo in the topleft of the main admin interface.
- ``LOGO_BACKGROUND`` sets a custom background colour for the brand logo
in the topleft of the main admin interface.
For a list of colour codes refer to this page of `w3schools`_.
- ``LOGO_URL`` sets a URL for a custom logo. This logo replaces the Mailu logo in the topleft of the main admin interface.
- ``LOGO_URL`` sets a URL for a custom logo. This logo replaces the Mailu
logo in the topleft of the main admin interface.
.. _`w3schools`: https://www.w3schools.com/cssref/css_colors.asp
@ -168,7 +173,8 @@ To have the account created automatically, you just need to define a few environ
- ``ifmissing``: creates a new admin account when the admin account does not exist.
- ``update``: creates a new admin account when it does not exist, or update the password of an existing admin account.
Note: It is recommended to set ``INITIAL_ADMIN_MODE`` to either ``update`` or ``ifmissing``. Leaving it with the default value will cause an error when the system is restarted.
Note: It is recommended to set ``INITIAL_ADMIN_MODE`` to either ``update`` or ``ifmissing``. Leaving it with the
default value will cause an error when the system is restarted.
An example:
@ -184,11 +190,19 @@ Depending on your particular deployment you most probably will want to change th
Advanced settings
-----------------
The ``CREDENTIAL_ROUNDS`` (default: 12) setting is the number of rounds used by the password hashing scheme. The number of rounds can be reduced in case faster authentication is needed or increased when additional protection is desired. Keep in mind that this is a mitigation against offline attacks on password hashes, aiming to prevent credential stuffing (due to password re-use) on other systems.
The ``CREDENTIAL_ROUNDS`` (default: 12) setting is the number of rounds used by the
password hashing scheme. The number of rounds can be reduced in case faster
authentication is needed or increased when additional protection is desired.
Keep in mind that this is a mitigation against offline attacks on password hashes,
aiming to prevent credential stuffing (due to password re-use) on other systems.
The ``SESSION_COOKIE_SECURE`` (default: True) setting controls the secure flag on the cookies of the administrative interface. It should only be turned off if you intend to access it over plain HTTP.
The ``SESSION_COOKIE_SECURE`` (default: True) setting controls the secure flag on
the cookies of the administrative interface. It should only be turned off if you
intend to access it over plain HTTP.
``SESSION_TIMEOUT`` (default: 3600) is the maximum amount of time in seconds between requests before a session is invalidated. ``PERMANENT_SESSION_LIFETIME`` (default: 108000) is the maximum amount of time in seconds a session can be kept alive for if it hasn't timed-out.
``SESSION_TIMEOUT`` (default: 3600) is the maximum amount of time in seconds between
requests before a session is invalidated. ``PERMANENT_SESSION_LIFETIME`` (default: 108000)
is the maximum amount of time in seconds a session can be kept alive for if it hasn't timed-out.
The ``LOG_LEVEL`` setting is used by the python start-up scripts as a logging threshold.
Log messages equal or higher than this priority will be printed.
@ -197,13 +211,20 @@ See the `python docs`_ for more information.
.. _`python docs`: https://docs.python.org/3.6/library/logging.html#logging-levels
The ``LETSENCRYPT_SHORTCHAIN`` (default: False) setting controls whether we send the ISRG Root X1 certificate in TLS handshakes. This is required for `android handsets older than 7.1.1` but slows down the performance of modern devices.
The ``LETSENCRYPT_SHORTCHAIN`` (default: False) setting controls whether we send the
ISRG Root X1 certificate in TLS handshakes. This is required for `android handsets older than 7.1.1`
but slows down the performance of modern devices.
.. _`android handsets older than 7.1.1`: https://community.letsencrypt.org/t/production-chain-changes/150739
.. _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 ``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.
@ -318,10 +339,13 @@ Mail log settings
By default, all services log directly to stdout/stderr. Logs can be collected by any docker log processing solution.
Postfix writes the logs to a syslog server which logs to stdout. This is used to filter out messages from the healthcheck.
In some situations, a separate mail log is required (e.g. for legal reasons). The syslog server can be configured to write log files to a volume. It can be configured with the following option:
Postfix writes the logs to a syslog server which logs to stdout. This is used to filter
out messages from the healthcheck. In some situations, a separate mail log is required
(e.g. for legal reasons). The syslog server can be configured to write log files to a volume.
It can be configured with the following option:
- ``POSTFIX_LOG_FILE``: The file to log the mail log to. When enabled, the syslog server will also log to stdout.
When ``POSTFIX_LOG_FILE`` is enabled, the logrotate program will automatically rotate the logs every week and keep 52 logs.
To override the logrotate configuration, create the file logrotate.conf with the desired configuration in the :ref:`Postfix overrides folder<override-label>`.
When ``POSTFIX_LOG_FILE`` is enabled, the logrotate program will automatically rotate the
logs every week and keep 52 logs. To override the logrotate configuration, create the file logrotate.conf
with the desired configuration in the :ref:`Postfix overrides folder<override-label>`.

@ -176,7 +176,7 @@ Finally, if you need to install packages inside the containers for debugging:
Reviewing
---------
Members of the **Mailu/contributors** team leave reviews to open PR's.
Members of the **Mailu/contributors** team leave reviews on open PR's.
In the case of a PR from a fellow team member, a single review is enough
to initiate merging. In all other cases, two approving reviews are required.
There is also a possibility to set the ``review/need2`` to require a second review.
@ -245,8 +245,8 @@ feel free to write a comment with ``bors retry``.
The command "git checkout -qf <hash>" failed and exited with 128 during .
Please wait a few minutes to do so, not to interfere with other builds.
Also, don't abuse this command if anything else went wrong,
Please wait a few minutes to do so, so as not to interfere with other builds.
Also, don't abuse this command if anything else goes wrong,
the author needs to try to fix it instead!
Reviewing by git
@ -289,7 +289,7 @@ importing the branch into your fork, do the merge and send a PR to the repositor
Merge the PR locally
```````````````````````
When someone sends a PR, you need merge his PR into master locally. This example will put you in a
When someone sends a PR, you need merge their PR into master locally. This example will put you in a
"detached head" state and do the merge in that state. Any commits done in this state will be lost
forever when you checkout a "normal" branch. This is exactly what we want, as we do not want to mess
with our repositories. This is just a test run.

@ -34,7 +34,7 @@ dependent on them.
Examples of features we will not include in Mailu are: calendars, documents and
file sharing, full-sized groupware, instant messaging, password management. If
you want and success in connecting those to Mailu and want to share, please
you want these features, have success in connecting them to Mailu and want to share, please
write some useful documentation for others to do the same.
What behavior is tolerated
@ -72,7 +72,7 @@ A Mailu container should provide one service and run one type of process only.
A new Webmail should be in a separate container, a new antivirus or a new
antispam should be in a separate container.
A container is developped as a singled directory under the proper category in
A container is developed as a single directory under the proper category in
the main repository, the only exception being service containers that should
only use official Docker images. Categories are:
@ -80,7 +80,7 @@ only use official Docker images. Categories are:
- optional, for optional components
- webmail, for webmails
A container image name must explicitely state the technology being used.
A container image name must explicitly state the technology being used.
Container versions are synchronized and all containers are always built at
once. The service name associated in the Compose file or Kubernetes configuration
should match the container image name.
@ -96,7 +96,7 @@ The `socrate` Python package should include relevant functions for container
lifecycle management.
Anything that is not static, i.e. able to change at runtime, either due to
configuration in the admin ui or user behavior, should take advantage of the
configuration in the admin UI or user behavior, should take advantage of the
admin API. The `podop` package binds mail specific sofware (Postfix and Dovecot
at the moment) to the admin API, other containers should use specific API calls.
@ -166,7 +166,7 @@ How should configuration be overridden
Some containers support configuration override. For this feature, we should
ideally look for conditional configuration inclusion in the configuration syntax
and use it. If the tool supports multiple methods of overrides, we should use
the one that supports overriding the more configuration.
the one that supports overriding the most configuration.
In case the tool does not support conditional inclusion, we can add the
override logic in the `start.py` script.

@ -17,7 +17,7 @@ Commits
This is a community project, thus commits should be readable enough for any of
the contributors to guess the content by simply reading the comment or find a
proper commit when one knows what he is looking for.
proper commit when one knows what they are looking for.
Usual standards remain: write english comments, single line short comments and
additional multiline if required (keep in mind that the most important piece
@ -32,7 +32,7 @@ master directly if you find this appropriate. Still, keep in mind that:
- a git email or a pull request should address a single feature a bug,
so keep your branches as tidy as possible;
- this is a small project with a limited number of forks and active threads
on Github, so one might want to look at your fork and find the branch you
on Github, so someone might want to look at your fork and find the branch you
are using to implement a specific feature; to avoid any hassle, we suggest
that you use branch names prefixed with ``feat-`` or ``fix-`` and followed
either by the name of the Github issue or a short and meaningful name.
@ -76,7 +76,7 @@ Forked projects
---------------
If you find yourself forking the project for a specific independant purpose
(commercial use, different phylosophy or incompatible point of view), we would
(commercial use, different philosophy or incompatible point of view), we would
be glad if you let us know so that we can list interesting known forks and
their specific features (and backport some of them if your implementation
is free as well).

@ -80,8 +80,8 @@ In ``pg_hba.conf`` there should be a line like this:
host mailu mailu <mailu_host>/32 md5
Note that this example is the bare-minimum to get Mailu working. It goes without saying that
the database admin will have to setup his own means of backups and TLS encrypted connections.
Note that this example is the bare-minimum to get Mailu working. Additional work needs to be
done by the database admin to setup their own means of backups and TLS encrypted connections.
Nowadays it is recommended to use the official PostgreSQL image from the PostgreSQL community. The repository is located `here <https://hub.docker.com/_/postgres>`_.
@ -91,9 +91,11 @@ Mailu PostgreSQL
----------------
Mailu optionally came with a pre-configured PostgreSQL image which was deprecated in Mailu 1.8.
Since Mailu 1.9 it is removed from Mailu. The following section describes how to move to a different PostgreSQL image for novice administrators. The official PostgreSQL image (Postgres) will be used.
Since Mailu 1.9 it is removed from Mailu. The following section describes how to move to a different
PostgreSQL image for novice administrators. The official PostgreSQL image (Postgres) will be used.
A Mailu deployment with the Mailu PostgreSQL image, only used PostgreSQL for the Admin container (Web administration interface). Roundcube used SQLite as database back-end.
A Mailu deployment with the Mailu PostgreSQL image, will only use PostgreSQL for the Admin container
(Web administration interface). Roundcube uses SQLite as database back-end.
Mailu uses the following configuration for connecting to the database:
- Database host: 'database'
@ -206,4 +208,4 @@ Optionally you can remove left-over files which were used by the old database:
.. note::
Roundcube does not offer a migration tool for moving from SQLite to PostgreSQL.
In case roundcube is used, then in the setup utility SQLite can be chosen as database back end for roundcube.
In case roundcube is used, the Mailu setup utility can be used to specify SQLite for the roundcube database backend.

@ -10,7 +10,7 @@ Where to ask questions?
```````````````````````
First, please read this FAQ to check if your question is listed here.
Simple questions best fit in our `Matrix`_ room.
Simple questions are best asked in our `Matrix`_ room.
For more complex questions, you can always open a `new issue`_ on GitHub.
We actively monitor the issues list.
@ -18,14 +18,14 @@ We actively monitor the issues list.
My installation is broken!
``````````````````````````
We're sorry to hear that. Please check for common mistakes and troubleshooting
We are sorry to hear that. Please check for common mistakes and troubleshooting
advice in the `Technical issues`_ section of this page.
I think I found a bug!
``````````````````````
If you did not manage to solve the issue using this FAQ and there is not any
`open issues`_ describing the same problem, you can continue to open a
If you did not manage to solve the issue using this FAQ and there are not any
`open issues`_ describing the same problem, you can open a
`new issue`_ on GitHub.
I want a new feature or enhancement!
@ -46,7 +46,7 @@ If you can't find anything similar, you can open a `new issue`_.
Please also share (where applicable):
- Use case: how does this improve the project?
- Any research done on the subject. Perhaps some links to upstream website,
- Any research done on the subject. Perhaps some links to upstream websites,
reference implementations etc.
Why does my feature/bug take so long to solve?
@ -179,7 +179,7 @@ incoming connection to the SMTP container will bypass the authentication stage b
settings and causes an Open Relay. And you really don't want this!
So, how to make it work? Well, by using `docker-ipv6nat`_! This nifty container will set up ``ip6tables``,
just as Docker would do for IPv4. We know that nat-ing is not advised in IPv6,
just as Docker would do for IPv4. We know that NAT-ing is not advised in IPv6,
however exposing all containers to public network neither. The choice is ultimately yous.
Mailu `setup utility`_ generates a safe IPv6 ULA subnet by default. So when you run the following command,
@ -211,26 +211,26 @@ For making the **storage** highly available, all sorts of techniques can be used
- btrfs in raid configuration
- Distributed network filesystems such as GlusterFS or CEPH
Note that no storage HA solution can protect against incidental deletes or file corruptions.
Note that no storage HA solution can protect against accidental deletes or file corruptions.
Therefore it is advised to create backups on a regular base!
A backup MX can be configured as **failover**. For this you need a separate server running
Mailu. On that server, your domains will need to be setup as "Relayed domains", pointing
to you main server. MX records for the mail domains with a higher priority number will have
to point to this server. Please be aware that a backup MX can act as a `spam magnet`_.
to you mainr server. MX records for the mail domains with a higher priority number will have
to point to this server. Please be aware that a backup MX can act as a `spam magnet`_ (archive.org).
For **service** HA, please see: `How does Mailu scale up?`_
*Issue reference:* `177`_, `591`_.
.. _`spam magnet`: https://blog.zensoftware.co.uk/2012/07/02/why-we-tend-to-recommend-not-having-a-secondary-mx-these-days/
.. _`spam magnet`: https://web.archive.org/web/20130131032707/https://blog.zensoftware.co.uk/2012/07/02/why-we-tend-to-recommend-not-having-a-secondary-mx-these-days/
Does Mailu run on Rancher?
``````````````````````````
There is a rancher catalog for Mailu in the `Mailu/Rancher`_ repository. The user group for Rancher is small,
so we cannot promise any support on this when you're heading into trouble. See the repository README for more details.
so we cannot promise any support on this when you are heading into trouble. See the repository README for more details.
*Issue reference:* `125`_.
@ -296,7 +296,7 @@ I want to integrate Nextcloud 15 (and newer) with Mailu
If a domain name (e.g. example.com) is specified, then this makes sure that only users from this domain will be allowed to login.
After successfull login the domain part will be striped and the rest used as username in Nextcloud. e.g. 'username@example.com' will be 'username' in Nextcloud. Disable this behaviour by changing true (the fifth parameter) to false.
After successfull login the domain part will be stripped and the rest used as username in Nextcloud. e.g. 'username@example.com' will be 'username' in Nextcloud. Disable this behaviour by changing true (the fifth parameter) to false.
*Issue reference:* `575`_.
@ -476,7 +476,7 @@ These issues are typically caused by four scenarios:
#. When ``TLS_FLAVOR=letsencrypt``, it might be that the *certbot* script is not capable of
obtaining the certificates for your domain. See `letsencrypt issues`_
#. When ``TLS_FLAVOR=cert``, certificates are supposed to be copied to ``/mailu/certs``.
Using an external ``letsencrypt`` program, it tends to happen people copy the whole
Using an external ``letsencrypt`` program, it tends to happen when people copy the whole
``letsencrypt/live`` directory containing symlinks. Symlinks do not resolve inside the
container and therefore it breaks the TLS implementation.
@ -728,7 +728,7 @@ In any case, using a dedicated DNS server will improve the performance of your m
Can I learn ham/spam messages from an already existing mailbox?
```````````````````````````````````````````````````````````````
Mailu is supporting automatic spam learning for messages moved to the Junk mailbox. Any email moved from the Junk Folder will learnt as ham.
Mailu supports automatic spam learning for messages moved to the Junk mailbox. Any email moved from the Junk Folder will learnt as ham.
If you already have an existing mailbox and want Mailu to learn them all as ham messages, you might run rspamc from within the dovecot container:
@ -779,7 +779,7 @@ Detailed instructions can be found at https://docs.docker.com/compose/install/
*Issue reference:* `853`_.
Why are still spam mails being discarded?
Why are spam mails being discarded?
`````````````````````````````````````````
Disabling antispam in the user settings actually disables automatic classification of messages as spam and stops moving them to the `junk` folder. It does not stop spam scanning and filtering.
@ -793,9 +793,9 @@ Why is SPF failing while properly setup?
Very often, SPF failure is related to Mailu sending emails with a different IP address than the one configured in the env file.
This is mostly due to using a separate IP address for Mailu and still having masquerading nat setup for Docker, which results in a different outbound IP address. You can simply check the email headers on the receiving side to confirm this.
This is mostly due to using a separate IP address for Mailu and still having masquerading NAT setup for Docker, which results in a different outbound IP address. You can simply check the email headers on the receiving side to confirm this.
If you wish to explicitely nat Mailu outbound traffic, it is usually easy to source-nat outgoing SMTP traffic using iptables :
If you wish to explicitely NAT Mailu outbound traffic, it is usually easy to source-NAT outgoing SMTP traffic using iptables :
```
iptables -t nat -A POSTROUTING -o eth0 -p tcp --dport 25 -j SNAT --to <your mx ip>
@ -829,7 +829,7 @@ iptables -t nat -A POSTROUTING -o eth0 -p tcp --dport 25 -j SNAT --to <your mx i
A user gets ``Sender address rejected: Access denied. Please check the`` ``message recipient […] and try again`` even though the sender is legitimate?
``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
First, check if you are really sure the user is a legitimate sender, i.e. the registered user is authenticated successfully and own either the account or alias he/she is trying to send from. If you are really sure this is correct, then the user might try to errornously send via port 25 insteadof the designated SMTP client-ports. Port 25 is meant for server-to-server delivery, while users should use port 587 or 465.
First, check if you are really sure the user is a legitimate sender, i.e. the registered user is authenticated successfully and own either the account or alias he/she is trying to send from. If you are really sure this is correct, then the user might try to erroneously send via port 25 instead of the designated SMTP client-ports. Port 25 is meant for server-to-server delivery, while users should use port 587 or 465.
The admin container won't start and its log says ``Critical: your DNS resolver isn't doing DNSSEC validation``
``````````````````````````````````````````````````````````````````````````````````````````````````````````````

@ -12,17 +12,17 @@ following principles, with decreasing priority:
3. a proper mail server offers proper security
4. a proper mail server has a simple and beautiful user interface
Mailu was designed and is kept up to date based on these. Among the most
Mailu was designed and is kept up to date based on these. Among the
structuring choices, the following were quite challenging:
- Mailu is based on containers (plural) so that any part can be reused
in separate projects and that updating or swapping any component does
not require to alter others.
- Mailu offers mail and does not bring any bloat in the default setup.
not require changing other ones.
- Mailu offers mail and does not bloat the default setup.
Additional features are available but not required.
- Mailu has a central front container that routes all HTTP and mail
traffic.
- Mailu has a Web administration interface that exposes both a Web ui
- Mailu has a Web administration interface that exposes both a Web UI
and internal API.
- Mailu authors are all equal in regard to copyright, thus the license
will remain free unless all contributors agree otherwise.

@ -31,7 +31,7 @@ Docker is able to forward logs to multiple log engines. Read the following docum
.. _external_certs:
Managing of external Let's encrypt certificates
Managing of external Let's Encrypt certificates
-----------------------------------------------
When you are not using the embedded ``letsencrypt`` option from Mailu,
@ -59,11 +59,14 @@ And the certbot command you will use in crontab would look something like:
Migrating an instance
---------------------
The SMTP protocol has an embedded retry mechanism and multiple MX that can serve a single domain, so that most migration processes or maintenance processes do not require any specific care.
The SMTP protocol has an embedded retry mechanism and multiple MX that can serve a single domain, so that most migration processes
or maintenance processes do not require any specific care.
Mailu relies heavily on files for storing everything, which helps the migration process, that can be performed based on file synchronization.
The suggested migration process consists of setting up a new backup server that drops incoming emails (Mailu not started), synchronizing both servers, stopping the main server and launching the backup server. Then, the backup server is switched as a main MX and the old server is deleted.
The suggested migration process consists of setting up a new backup server that drops incoming emails (Mailu not started),
synchronizing both servers, stopping the main server and launching the backup server. Then, the backup server is switched
as a main MX and the old server is deleted.
1. Prepare your new server, copy your ``docker-compose.yml``, ``.env`` and basic configuration files to the server, so that it is ready to start configuration Mailu, *do not start Mailu*
2. Setup your DNS so that the backup server is an additional, deprioritized MX for the domain; this can be complex if you serve many domains, in which case you can simply accept that some remote MX will retry for a couple of minutes, skip this step

@ -1,9 +1,18 @@
Using an external reverse proxy
===============================
One of Mailu use cases is as part of a larger services platform, where maybe other Web services are available than Mailu Webmail and admin interface.
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. 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 Docker Compose configuration structure, it is impossible for us to make disabling the Web frontend completely available through a configuration variable. This guide was written to help users setup such an architecture.
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
help users setup such an architecture.
There are basically three options, from the most to the least recommended one:
@ -19,7 +28,8 @@ This is configured in the mailu.env file. See the :ref:`configuration reference
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. For instance, in the ``front`` section of Mailu ``docker-compose.yml``, use local ports 8080 and 8443 respectively for HTTP and HTTPS:
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
@ -35,7 +45,8 @@ The simplest and safest option is to modify the port forwards for Mailu Web fron
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 (as the HTTP port simply redirects there). Here is an example Nginx configuration:
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
@ -98,7 +109,8 @@ Because the admin interface is served as ``/admin``, the Webmail as ``/webmail``
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 :
@ -153,17 +165,20 @@ Traefik as 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.
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.
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``.
@ -179,12 +194,13 @@ Add the respective Traefik labels for your domain/configuration, like
.. 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.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``?
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
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 client IP. This is configured in mailu.env:
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
@ -219,7 +235,8 @@ 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. 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`: https://traefik.io/

@ -25,8 +25,8 @@ You are free to choose any operating system that runs Docker (>= 1.11),
then chose between various flavors including Docker Compose, Kubernetes
and Rancher.
Compose is the most tested flavor and should be used by any unexperimented
user. Make sure you complete the requirements for the flavor you chose.
Compose is the most tested flavor and should be the choice for less experienced
users. Make sure you complete the requirements for the flavor you chose.
You should also have at least a DNS hostname and a DNS name for receiving
emails. Some instructions are provided on the matter in the article

@ -45,10 +45,12 @@ It offers the following configuration options:
Access the web administration interface
---------------------------------------
The admin GUI is by default accessed via the URL `https://<my domain>/admin`, when it's enabled in the setup utility or by manually setting `ADMIN=true` in `mailu.env`.
The admin GUI is by default accessed via the URL `https://<my domain>/admin`, when it's enabled in the setup utility
or by manually setting `ADMIN=true` in `mailu.env`.
To login the admin GUI enter the email address and password of an user.
Only global administrator users have access to all configuration settings and the Rspamd webgui. Other users will be presented with settings for only their account, and domains they are managers of.
Only global administrator users have access to all configuration settings and the Rspamd webgui. Other users will be
presented with settings for only their account, and domains they are managers of.
To create a user who is a global administrator for a new installation, the Mailu.env file can be adapted.
For more information see the section 'Admin account - automatic creation' in :ref:`the configuration reference <admin_account>`.

@ -11,7 +11,7 @@ RUN apk add --no-cache \
python3 py3-pip git bash py3-multidict tzdata \
&& pip3 install --upgrade pip
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, snappymail, roundcube
RUN pip3 install socrate==0.2.0
# Image specific layers under this line

@ -49,7 +49,7 @@ DISABLE_STATISTICS={{ disable_statistics or 'False' }}
# Expose the admin interface (value: true, false)
ADMIN={{ admin_enabled or 'false' }}
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL={{ webmail_type }}
# Dav server implementation (value: radicale, none)

@ -10,13 +10,9 @@ the Web. By exposing a complex application such as a Webmail, you should be awar
the security implications caused by such an increase of attack surface.<p>
<div class="form-group">
<label>Enable Web email client (and path to the Web email client)</label>
<!-- <div class="radio"> -->
<!-- {{ macros.radio("webmail_type", "roundcube", "RoundCube", "popular Webmail running on top of PHP") }} -->
<!-- {{ macros.radio("webmail_type", "rainloop", "Rainloop", "lightweight Webmail based on PHP, no database") }} -->
<!-- </div> -->
<br/>
<select class="btn btn-primary dropdown-toggle" name="webmail_type" id="webmail">
{% for webmailtype in ["none", "roundcube", "rainloop"] %}
{% for webmailtype in ["none", "roundcube", "snappymail"] %}
<option value="{{ webmailtype }}" >{{ webmailtype }}</option>
{% endfor %}
</select>

@ -12,7 +12,7 @@ the security implications caused by such an increase of attack surface.<p>
<label>Enable Web email client (and path to the Web email client)</label>
<br/>
<select class="btn btn-primary dropdown-toggle" name="webmail_type" id="webmail">
{% for webmailtype in ["none", "roundcube", "rainloop"] %}
{% for webmailtype in ["none", "roundcube", "snappymail"] %}
<option value="{{ webmailtype }}" >{{ webmailtype }}</option>
{% endfor %}
</select>

@ -53,7 +53,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)

@ -53,7 +53,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)

@ -53,7 +53,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)

@ -53,7 +53,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=false
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=roundcube
# Dav server implementation (value: radicale, none)

@ -88,7 +88,7 @@ services:
# Webmail
webmail:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rainloop:${MAILU_VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}snappymail:${PINNED_MAILU_VERSION:-local}
restart: always
env_file: mailu.env
volumes:

@ -53,8 +53,8 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=false
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=rainloop
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=snappymail
# Dav server implementation (value: radicale, none)
WEBDAV=none

@ -53,7 +53,7 @@ DISABLE_STATISTICS=False
# Expose the admin interface (value: true, false)
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
# Choose which webmail to run if any (values: roundcube, snappymail, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)

@ -0,0 +1 @@
Switch from RainLoop to SnappyMail. SnappyMail has better performance and is more secure.

@ -1,79 +0,0 @@
ARG ARCH=""
# NOTE: only add file if building for arm
FROM ${ARCH}alpine:3.14
ARG VERSION
ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
ENV TZ Etc/UTC
LABEL version=$VERSION
# Shared later between dovecot postfix nginx rspamd rainloop and roundloop
RUN apk add --no-cache \
python3 py3-pip tzdata \
&& pip3 install socrate==0.2.0
# https://www.rainloop.net/docs/system-requirements/
# Rainloop:
# cURL Builtin
# iconv php7-iconv
# json php7-json
# libxml php7-xml
# dom php7-dom
# openssl php7-openssl
# DateTime Builtin
# PCRE Builtin
# SPL Builtin
# Recommended:
# php7-fpm FastCGI Process Manager
# Optional PHP extension (for contacts):
# php7-pdo Accessing databases in PHP
# php7-pdo_sqlite Access to SQLite 3 databases
RUN apk add --no-cache \
nginx \
php7 php7-fpm php7-curl php7-iconv php7-json php7-xml php7-simplexml php7-dom php7-openssl php7-pdo php7-pdo_sqlite \
&& rm /etc/nginx/http.d/default.conf \
&& rm /etc/php7/php-fpm.d/www.conf \
&& mkdir -p /run/nginx \
&& mkdir -p /var/www/rainloop \
&& mkdir -p /config
# nginx / PHP config files
COPY config/nginx-rainloop.conf /config/nginx-rainloop.conf
COPY config/php-rainloop.conf /etc/php7/php-fpm.d/rainloop.conf
# Rainloop login
COPY login/include.php /var/www/rainloop/include.php
COPY login/sso.php /var/www/rainloop/sso.php
# Parsed en moved at startup
COPY defaults/php.ini /defaults/php.ini
COPY defaults/application.ini /defaults/application.ini
COPY defaults/default.ini /defaults/default.ini
# Install Rainloop from source
ENV RAINLOOP_URL https://github.com/RainLoop/rainloop-webmail/releases/download/v1.16.0/rainloop-community-1.16.0.zip
RUN apk add --no-cache \
curl unzip \
&& cd /var/www/rainloop \
&& curl -L -O ${RAINLOOP_URL} \
&& unzip -q *.zip \
&& rm -f *.zip \
&& rm -rf data/ \
&& find . -type d -exec chmod 755 {} \; \
&& find . -type f -exec chmod 644 {} \; \
&& chown -R nginx:nginx /var/www/rainloop \
&& apk del unzip
COPY start.py /start.py
COPY config.py /config.py
EXPOSE 80/tcp
VOLUME ["/data"]
CMD /start.py
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1
RUN echo $VERSION >> /version

@ -1,41 +0,0 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/rainloop;
# /dev/stdout (Default), <path>, off
access_log off;
# /dev/stderr (Default), <path>, debug, info, notice, warn, error, crit, alert, emerg
error_log /dev/stderr warn;
index index.php;
# set maximum body size to configured limit
client_max_body_size {{ MESSAGE_SIZE_LIMIT|int + 8388608 }};
location / {
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_intercept_errors on;
fastcgi_index index.php;
fastcgi_keep_conn on;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
location ^~ /data {
deny all;
}
}

@ -1,31 +0,0 @@
<?php
$_ENV['RAINLOOP_INCLUDE_AS_API'] = true;
if (!defined('APP_VERSION')) {
$version = file_get_contents('/data/VERSION');
if ($version) {
define('APP_VERSION', $version);
define('APP_INDEX_ROOT_FILE', __FILE__);
define('APP_INDEX_ROOT_PATH', str_replace('\\', '/', rtrim(dirname(__FILE__), '\\/').'/'));
}
}
if (file_exists(APP_INDEX_ROOT_PATH.'rainloop/v/'.APP_VERSION.'/include.php')) {
include APP_INDEX_ROOT_PATH.'rainloop/v/'.APP_VERSION.'/include.php';
} else {
echo '[105] Missing version directory';
exit(105);
}
// Retrieve email and password
if (isset($_SERVER['HTTP_X_REMOTE_USER']) && isset($_SERVER['HTTP_X_REMOTE_USER_TOKEN'])) {
$email = $_SERVER['HTTP_X_REMOTE_USER'];
$password = $_SERVER['HTTP_X_REMOTE_USER_TOKEN'];
$ssoHash = \RainLoop\Api::GetUserSsoHash($email, $password);
// redirect to webmail sso url
header('Location: index.php?sso&hash='.$ssoHash);
}
else {
header('HTTP/1.0 403 Forbidden');
}

@ -0,0 +1,86 @@
ARG ARCH=""
# NOTE: only add file if building for arm
FROM ${ARCH}alpine:3.14
ARG VERSION
ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
ENV TZ Etc/UTC
LABEL version=$VERSION
# Shared later between dovecot postfix nginx rspamd snappymail and roundloop
RUN apk add --no-cache \
python3 py3-pip tzdata \
&& pip3 install socrate==0.2.0
# https://github.com/the-djmaze/snappymail/wiki/Installation-instructions#requirements
# SnappyMail:
# SnappyMail requires PHP 7.4 (or a newer version) with the following extensions:
#
# mbstring php7-mbstring
# Zlib built-in OR php7-zip????
# json php7-json
# libxml php7-xml
# dom php7-dom
# Optional extensions:
# cURL php7-curl
# exif php7-exif
# gd, gmagick or imagemagick gd and php7-gd
# gnupg gpgme and alpine has no php7-gnupg library :(
# iconv php7-iconv
# intl php7-intl
# ldap we don't use ldap
# openssl php7-openssl
# PDO (MySQL/PostgreSQL/SQLite) (for contacts) php7-pdo_sqlite and php7-pdo
# redis NOT USED
# Sodium php7-sodium and libsodium
# Tidy php7-tidy
# uuid (PECL) php7-pecl-uuid
# xxtea (PECL) not found on alpine repo
# zip php7-zip
#php7-curl php7-iconv php7-json php7-xml php7-simplexml php7-dom php7-openssl php7-pdo php7-pdo_sqlite php7-mbstring \
RUN apk add --no-cache \
nginx curl \
php7 php7-fpm php7-mbstring php7-zip php7-json php7-xml php7-simplexml \
php7-dom php7-curl php7-exif gd php7-gd php7-iconv php7-intl php7-openssl \
php7-pdo_sqlite php7-pdo php7-sodium libsodium php7-tidy php7-pecl-uuid \
&& rm /etc/nginx/http.d/default.conf \
&& rm /etc/php7/php-fpm.d/www.conf \
&& mkdir -p /run/nginx \
&& mkdir -p /var/www/webmail \
&& mkdir -p /config
# nginx / PHP config files
COPY config/nginx-snappymail.conf /config/nginx-snappymail.conf
COPY config/php-snappymail.conf /etc/php7/php-fpm.d/snappymail.conf
# Parsed and moved at startup
COPY defaults/php.ini /defaults/php.ini
COPY defaults/application.ini /defaults/application.ini
COPY defaults/default.ini /defaults/default.ini
# Install Snappymail from source
ENV SNAPPYMAIL_URL https://github.com/the-djmaze/snappymail/releases/download/v2.13.4/snappymail-2.13.4.zip
RUN cd /var/www/webmail \
&& busybox wget ${SNAPPYMAIL_URL} -O - | busybox unzip - \
&& chmod -R u+w,a+rX /var/www/webmail \
&& chown -R nginx:nginx /var/www/webmail
# SnappyMail login
COPY login/include.php /var/www/webmail/include.php
COPY login/sso.php /var/www/webmail/sso.php
COPY start.py /start.py
COPY config.py /config.py
EXPOSE 80/tcp
VOLUME ["/data"]
CMD /start.py
HEALTHCHECK CMD curl -f -L http://localhost/ping || exit 1
RUN echo $VERSION >> /version

@ -10,6 +10,6 @@ args = os.environ.copy()
log.basicConfig(stream=sys.stderr, level=args.get("LOG_LEVEL", "WARNING"))
# Build final configuration paths
conf.jinja("/config/nginx-rainloop.conf", args, "/etc/nginx/http.d/rainloop.conf")
conf.jinja("/config/nginx-snappymail.conf", args, "/etc/nginx/http.d/snappymail.conf")
if os.path.exists("/var/run/nginx.pid"):
os.system("nginx -s reload")

@ -0,0 +1,62 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/webmail;
include /etc/nginx/mime.types;
# /dev/stdout (Default), <path>, off
access_log off;
# /dev/stderr (Default), <path>, debug, info, notice, warn, error, crit, alert, emerg
error_log /dev/stderr notice;
index index.php;
# set maximum body size to configured limit
client_max_body_size {{ MESSAGE_SIZE_LIMIT|int + 8388608 }};
location / {
try_files $uri $uri/ /index.php$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include /etc/nginx/fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_index index.php;
fastcgi_keep_conn on;
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
{% if WEB_WEBMAIL == '/' %}
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
{% else %}
fastcgi_param SCRIPT_NAME {{WEB_WEBMAIL}}/$fastcgi_script_name;
{% endif %}
}
location ~ /\. {
deny all;
}
location ^~ /data {
deny all;
}
location = /ping {
allow 127.0.0.1;
deny all;
include /etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}

@ -1,11 +1,11 @@
; Start a new pool named 'rainloop'.
; Start a new pool named 'snappymail'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('rainloop' here)
[rainloop]
; pool name ('snappymail' here)
[snappymail]
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Default value: no.
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Default value: no.
catch_workers_output = 1
; Unix user/group of processes
@ -87,15 +87,32 @@ pm.max_children = 5
; Note: Mandatory when pm is set to 'dynamic'
; pm.max_spare_servers = 3
; This sets the maximum time in seconds a script is allowed to run before it is
; terminated by the parser. This helps prevent poorly written scripts from tying up
; This sets the maximum time in seconds a script is allowed to run before it is
; terminated by the parser. This helps prevent poorly written scripts from tying up
; the server. The default setting is 30s.
; Note: Used only when pm is set to 'ondemand'
pm.process_idle_timeout = 10s
; The number of requests each child process should execute before respawning.
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For endless
; request processing specify '0'.
; Equivalent to PHP_FCGI_MAX_REQUESTS. Default value: 0.
; Noted: Used only when pm is set to 'ondemand'
pm.max_requests = 200
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong

@ -1,4 +1,4 @@
; RainLoop Webmail configuration file
; Snappymail Webmail configuration file
[webmail]
attachment_size_limit = {{ MAX_FILESIZE }}
@ -8,7 +8,11 @@ allow_admin_panel = Off
[labs]
allow_gravatar = Off
{% if WEB_WEBMAIL == '/' %}
custom_login_link='sso.php'
{% else %}
custom_login_link='{{ WEB_WEBMAIL }}/sso.php'
{% endif %}
custom_logout_link='/sso/logout'
[contacts]

@ -0,0 +1,17 @@
<?php
$_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = true;
require 'index.php';
// Retrieve email and password
if (isset($_SERVER['HTTP_X_REMOTE_USER']) && isset($_SERVER['HTTP_X_REMOTE_USER_TOKEN'])) {
$email = $_SERVER['HTTP_X_REMOTE_USER'];
$password = $_SERVER['HTTP_X_REMOTE_USER_TOKEN'];
$ssoHash = \RainLoop\Api::CreateUserSsoHash($email, $password);
// redirect to webmail sso url
header('Location: index.php?sso&hash='.$ssoHash);
}
else {
header('HTTP/1.0 403 Forbidden');
}
?>

@ -27,7 +27,7 @@ conf.jinja("/defaults/php.ini", os.environ, "/etc/php7/php.ini")
os.system("php-fpm7")
os.system("chown -R nginx:nginx /data")
os.system("chmod -R a+rX /var/www/rainloop/")
os.system("chmod -R a+rX /var/www/webmail/")
subprocess.call(["/config.py"])
os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"])
Loading…
Cancel
Save