.. _kubernetes:

Kubernetes setup
================

Prequisites
-----------

Structure
~~~~~~~~~

There’s chosen to have a double NGINX stack for Mailu, this way the main
ingress can still be used to access other websites/domains on your
cluster. This is the current structure:

-  ``NGINX Ingress controller``: Listens to the nodes ports 80 & 443. We have chosen to have a double NGINX stack for Mailu.
-  ``Cert manager``: Creates automatic Lets Encrypt certificates based on an ``Ingress``-objects domain name.
-  ``Mailu NGINX Front daemonset``: This daemonset runs in parallel with the Nginx Ingress Controller and only listens on all E-mail specific ports (25, 110, 143, 587,...)
-  ``Mailu components``: All Mailu components (imap, smtp, security, webmail,...) are split into separate files to make them more handy to use, you can find the ``YAML`` files in this directory

What you need
~~~~~~~~~~~~~

-  A working Kubernetes cluster (tested with 1.10.5)
-  A working `cert-manager`_ installation
-  A working nginx-ingress controller needed for the lets-encrypt
   certificates. You can find those files in the ``nginx`` subfolder

Cert manager
^^^^^^^^^^^^

The ``Cert-manager`` is quite easy to deploy using Helm when reading the
`docs`_. After booting the ``Cert-manager`` you’ll need a
``ClusterIssuer`` which takes care of all required certificates through
``Ingress`` items. We chose to provide a ``clusterIssuer`` so you can provide SSL certificates
for other namespaces (different websites/services), if you don't need this option, you can easily change this by
changing ``clusterIssuer`` to ``Issuer`` and adding the ``namespace: mailu-mailserver`` to the metadata.
An example of a production and a staging ``clusterIssuer``:

.. code:: yaml

   # This clusterIssuer example uses the staging environment for testing first
   apiVersion: certmanager.k8s.io/v1alpha1
   kind: ClusterIssuer
   metadata:
     name: letsencrypt-stage
   spec:
     acme:
       email: something@example.com
       http01: {}
       privateKeySecretRef:
         name: letsencrypt-stage
       server: https://acme-staging-v02.api.letsencrypt.org/directory

.. code:: yaml

   # This clusterIssuer example uses the production environment
   apiVersion: certmanager.k8s.io/v1alpha1
   kind: ClusterIssuer
   metadata:
     name: letsencrypt-prod
   spec:
     acme:
       email: something@example.com
       http01: {}
       privateKeySecretRef:
         name: letsencrypt-prod
       server: https://acme-v02.api.letsencrypt.org/directory

**IMPORTANT**: All ``*-ingress.yaml`` files use the ``letsencrypt-stage`` ``clusterIssuer``. If you are ready for production,
change this field in all ``*-ingress.yaml`` files to ``letsencrypt-prod`` or whatever name you chose for the production.
If you choose for ``Issuer`` instead of ``clusterIssuer`` you also need to change the annotation to ``certmanager.k8s.io/issuer`` instead of ``certmanager.k8s.io/cluster-issuer``

Deploying Mailu
---------------

All manifests can be found in the ``mailu`` subdirectory. All commands
below need to be run from this subdirectory

Personalization
~~~~~~~~~~~~~~~

-  All services run in the same namespace, currently ``mailu-mailserver``. So if you want to use a different one, change the ``namespace`` value in **every** file
-  Check the ``storage-class`` field in the ``pvc.yaml`` file, you can also change the sizes to your liking. Note that you need ``RWX`` (read-write-many) and ``RWO`` (read-write-once) storageclasses.
-  Check the ``configmap.yaml`` and adapt it to your needs. Be sure to check the kubernetes DNS values at the end (if you use a different namespace)
-  Check the ``*-ingress.yaml`` files and change it to the domain you want (this is for the kubernetes ingress controller to handle the admin, webmail, webdav and auth connections)

Installation
------------

Boot the Mailu components
~~~~~~~~~~~~~~~~~~~~~~~~~

To start Mailu, run the following commands from the ``docs/kubernetes/mailu`` directory

.. code-block:: bash

    kubectl create -f rbac.yaml
    kubectl create -f configmap.yaml
    kubectl create -f pvc.yaml
    kubectl create -f redis.yaml
    kubectl create -f front.yaml
    kubectl create -f webmail.yaml
    kubectl create -f imap.yaml
    kubectl create -f security.yaml
    kubectl create -f smtp.yaml
    kubectl create -f fetchmail.yaml
    kubectl create -f admin.yaml
    kubectl create -f webdav.yaml
    kubectl create -f admin-ingress.yaml
    kubectl create -f webdav-ingress.yaml
    kubectl create -f security-ingress.yaml
    kubectl create -f webmail-ingress.yaml


Create the first admin account
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When the cluster is online you need to create you master user to access https://mail.example.com/admin
Enter the main ``admin`` pod to create the root account:

.. code-block:: bash

    kubectl -n mailu-mailserver get po
    kubectl -n mailu-mailserver exec -it mailu-admin-.... /bin/sh

And in the pod run the following command. The command uses following entries:

.. code-block:: bash

    python manage.py admin root example.com password

- ``admin`` Make it an admin user
- ``root`` The first part of the e-mail adres (ROOT@example.com)
- ``example.com`` the domain appendix
- ``password`` the chosen password for the user


Now you should be able to login on the mail account: https://mail.example.com/admin

Adaptations
-----------

Dovecot
~~~~~~~

- If you are using Dovecot on a shared file system (Glusterfs, NFS,...), you need to create a special override otherwise a lot of indexing errors will occur on your Dovecot pod.
- I also higher the number of max connections per IP. Now it's limited to 10.

Enter the dovecot pod:

.. code:: bash

    kubectl -n mailu-mailserver get po
    kubectl -n mailu-mailserver exec -it mailu-imap-.... /bin/sh

Create the file ``overrides/dovecot.conf``

.. code:: bash

    vi /overrides/dovecot.conf

And enter following contents:

.. code:: bash

    mail_nfs_index = yes
    mail_nfs_storage = yes
    mail_fsync = always
    mmap_disable = yes
    mail_max_userip_connections=100

Save and close the file and delete the imap pod to get it recreated.

.. code:: bash

    kubectl -n mailu-mailserver delete po/mailu-imap-....

Wait for the pod to recreate and you're online!
Happy mailing!

.. _here: https://github.com/hacor/Mailu/blob/master/core/postfix/conf/main.cf#L35
.. _cert-manager: https://github.com/jetstack/cert-manager
.. _docs: https://cert-manager.readthedocs.io/en/latest/getting-started/2-installing.html

Imap login fix
~~~~~~~~~~~~~~

If it seems you're not able to login using IMAP on your Mailu accounts, check the logs of the imap container to see whether it's a permissions problem on the database.
This problem can be easily fixed by running following commands:

.. code:: bash

    kubectl -n mailu-mailserver exec -it mailu-imap-... /bin/sh
    chmod 777 /data/main.db

If the login problem still persists, or more specific, happens now and then and you see some Auth problems on your webmail or mail client, try following steps:

- Add ``auth_debug=yes`` to the ``/overrides/dovecot.conf`` file and delete the pod in order to start a new one, which loads the configuration
- Depending on your network configuration you could still see some ``allow_nets check failed`` results in the logs. This means that the IP is not allowed a login
- If this is happening your network plugin has troubles with the Nginx Ingress Controller using the ``hostNetwork: true`` option. Known cases: Flannel and Calico.
- You should uncomment ``POD_ADDRESS_RANGE`` in the ``configmap.yaml`` file and add the IP range of your pod network bridge (the range that sadly has failed the ``allowed_nets`` test)
- Delete the IMAP pod and wait for it to restart

.. code:: bash

    kubectl -n mailu-mailserver get po
    kubectl -n mailu-mailserver delete po/mailu-imap...

Happy mailing!