Merge branch 'master' into feat-healthchecks
						commit
						81b24f61e8
					
				| @ -0,0 +1,24 @@ | ||||
| """ Add a start day for vacations | ||||
| 
 | ||||
| Revision ID: 3b281286c7bd | ||||
| Revises: 25fd6c7bcb4a | ||||
| Create Date: 2018-09-27 22:20:08.158553 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| revision = '3b281286c7bd' | ||||
| down_revision = '25fd6c7bcb4a' | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| 
 | ||||
| 
 | ||||
| def upgrade(): | ||||
|     with op.batch_alter_table('user') as batch: | ||||
|         batch.add_column(sa.Column('reply_startdate', sa.Date(), nullable=False, | ||||
|             server_default="1900-01-01")) | ||||
| 
 | ||||
| 
 | ||||
| def downgrade(): | ||||
|     with op.batch_alter_table('user') as batch: | ||||
|         batch.drop_column('reply_startdate') | ||||
| @ -0,0 +1,4 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| tee >(rspamc -h antispam:11334 -P mailu learn_ham /dev/stdin) \ | ||||
|     | rspamc -h antispam:11334 -P mailu -f 13 fuzzy_add /dev/stdin | ||||
| @ -1,3 +0,0 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| rspamc -h antispam:11334 -P mailu "learn_$1" /dev/stdin <&0 | ||||
| @ -0,0 +1,4 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| tee >(rspamc -h antispam:11334 -P mailu learn_spam /dev/stdin) \ | ||||
|     >(rspamc -h antispam:11334 -P mailu -f 11 fuzzy_add /dev/stdin) | ||||
| @ -1,3 +1,3 @@ | ||||
| require "vnd.dovecot.execute"; | ||||
| 
 | ||||
| execute :pipe "mailtrain" "spam"; | ||||
| execute :pipe "spam"; | ||||
|  | ||||
| @ -1,2 +1,9 @@ | ||||
| {% set version=github_version %} | ||||
| {% extends "!layout.html" %} | ||||
| {% block document %} | ||||
| {% if version != stable_version %} | ||||
| <div class="wy-alert info"> | ||||
|     <p>You are currently browsing documentation for the <b>{{ version }}</b> branch. Documentation for the stable <b>{{ stable_version }}</b> branch can be found <a href="/{{ stable_version }}/">here</a>.</p> | ||||
| </div> | ||||
| {% endif %} | ||||
| {{ super() }} | ||||
| {% endblock %} | ||||
|  | ||||
| @ -1,4 +0,0 @@ | ||||
| {%- extends "layout.html" %} | ||||
| {% block body %} | ||||
|   {{ body|replace("VERSION_TAG", version) }} | ||||
| {% endblock %} | ||||
| @ -0,0 +1,16 @@ | ||||
|   <div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions"> | ||||
|     <span class="rst-current-version" data-toggle="rst-current-version"> | ||||
|       <span class="fa fa-book"> Versions</span> | ||||
|       v: {{ version }} | ||||
|       <span class="fa fa-caret-down"></span> | ||||
|     </span> | ||||
|     <div class="rst-other-versions"> | ||||
|       <dl> | ||||
|         <dt>{{ _('Versions') }}</dt> | ||||
|         {% for slug, url in versions %} | ||||
|           <dd><a href="{{ url }}">{{ slug }}</a></dd> | ||||
|         {% endfor %} | ||||
|       </dl> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
| @ -0,0 +1,21 @@ | ||||
| version: '3' | ||||
| 
 | ||||
| 
 | ||||
| services: | ||||
|   docs_master: | ||||
|     image: mailu/docs:master | ||||
|     labels: | ||||
|       - traefik.enable=true | ||||
|       - traefik.port=80 | ||||
|       - traefik.main.frontend.rule=Host:${hostname};PathPrefix:/master/ | ||||
| 
 | ||||
|   docs_15: | ||||
|     image: mailu/docs:1.5 | ||||
|     labels: | ||||
|       - traefik.enable=true | ||||
|       - traefik.port=80 | ||||
|       - traefik.root.frontend.redirect.regex=.* | ||||
|       - traefik.root.frontend.redirect.replacement=/1.5/ | ||||
|       - traefik.root.frontend.rule=Host:${hostname};PathPrefix:/ | ||||
|       - traefik.main.frontend.rule=Host:${hostname};PathPrefix:/1.5/ | ||||
| 
 | ||||
| @ -1,157 +0,0 @@ | ||||
| # Install Mailu master on kubernetes | ||||
| 
 | ||||
| ## 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 and directly forwards all TCP traffic on the E-amail ports (993,143,25,587,...). This is because this `DaemonSet` already consumes ports 80 & 443 and uses `hostNetwork: true` | ||||
| - `Cert manager`: Creates automatic Lets Encrypt certificates based on an `Ingress`-objects domain name. | ||||
| - `Mailu NGINX Front container`: This container receives all the mail traffic forwarded from the ingress controller. The web traffic is also forwarded based on an ingress | ||||
| - `Mailu components`: All Mailu components are split into separate files to make them more  | ||||
| 
 | ||||
| ### What you need | ||||
| - A working Kubernetes cluster (tested with 1.10.5) | ||||
| - A working [cert-manager](https://github.com/jetstack/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](https://cert-manager.readthedocs.io/en/latest/getting-started/2-installing.html).  | ||||
| After booting the `Cert-manager` you'll need a `ClusterIssuer` which takes care of all required certificates through `Ingress` items. An example: | ||||
| 
 | ||||
| ```yaml | ||||
| apiVersion: certmanager.k8s.io/v1alpha1 | ||||
| kind: ClusterIssuer | ||||
| metadata: | ||||
|   name: letsencrypt-prod | ||||
| spec: | ||||
|   acme: | ||||
|     email: something@example.com | ||||
|     http01: {} | ||||
|     privateKeySecretRef: | ||||
|       key: "" | ||||
|       name: letsencrypt-stage | ||||
|     server: https://acme-v02.api.letsencrypt.org/directory | ||||
| ``` | ||||
| 
 | ||||
| ## 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-ssl.yaml` and change it to the domain you want (this is for the kubernetes ingress controller, it will forward to `mailu/nginx` a.k.a. the `front` pod) | ||||
| 
 | ||||
| ## Installation | ||||
| First run the command to start Mailu: | ||||
| 
 | ||||
| ```bash | ||||
| kubectl create -f rbac.yaml | ||||
| kubectl create -f configmap.yaml | ||||
| kubectl create -f pvc.yaml | ||||
| kubectl create -f ingress-ssl.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 | ||||
| ``` | ||||
| 
 | ||||
| ## 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: | ||||
| 
 | ||||
| ```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: | ||||
| - `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 | ||||
| 
 | ||||
| ```bash | ||||
| python manage.py admin root example.com password | ||||
| ``` | ||||
| 
 | ||||
| Now you should be able to login on the mail account: `https://mail.example.com/admin` | ||||
| 
 | ||||
| ## Adaptations | ||||
| 
 | ||||
| ### Postfix | ||||
| I noticed you need an override for the `postfix` server in order to be able to send mail. I noticed Google wasn't able to deliver mail to my account and it had to do with the `smtpd_authorized_xclient_hosts` value in the config file. The config can be read [here](https://github.com/hacor/Mailu/blob/master/core/postfix/conf/main.cf#L35) and is pointing to a single IP of the service. But the requests come from the host IPs (the NGINX Ingress proxy) and they don't use the service specific IP. | ||||
| 
 | ||||
| Enter the `postfix` pod: | ||||
| 
 | ||||
| ```bash | ||||
| kubectl -n mailu-mailserver get po | ||||
| kubectl -n mailu-mailserver exec -it mailu-smtp-.... /bin/sh | ||||
| ``` | ||||
| 
 | ||||
| Now you're in the pod, create an override file like so: | ||||
| 
 | ||||
| ```bash | ||||
| vi /overrides/postfix.cf | ||||
| ``` | ||||
| 
 | ||||
| And give it the following contents, off course replacing `10.2.0.0/16` with the CIDR of your pod range. This way the NGINX pods can also restart and your mail server will still operate | ||||
| 
 | ||||
| ```bash | ||||
| not_needed = true | ||||
| smtpd_authorized_xclient_hosts = 10.2.0.0/16 | ||||
| ``` | ||||
| 
 | ||||
| The first line seems stupid, but is needed because its pasted after a #, so from the second line we're really in action. | ||||
| Save and close the file and exit. Now you need to delete the pod in order to recreate the config file. | ||||
| 
 | ||||
| ```bash | ||||
| kubectl -n mailu-mailserver delete po/mailu-smtp-.... | ||||
| ``` | ||||
| 
 | ||||
| ### 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: | ||||
| 
 | ||||
| ```bash | ||||
| kubectl -n mailu-mailserver get po | ||||
| kubectl -n mailu-mailserver exec -it mailu-imap-.... /bin/sh | ||||
| ``` | ||||
| 
 | ||||
| Create the file `/overrides/dovecot.conf` | ||||
| 
 | ||||
| ```bash | ||||
| vi /overrides/dovecot.conf | ||||
| ``` | ||||
| 
 | ||||
| And enter following contents: | ||||
| ```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. | ||||
| 
 | ||||
| ```bash | ||||
| kubectl -n mailu-mailserver delete po/mailu-imap-.... | ||||
| ``` | ||||
| 
 | ||||
| Wait for the pod to recreate and you're online! | ||||
| Happy mailing! | ||||
| 
 | ||||
| Wait for the pod to recreate and you're online! | ||||
| Happy mailing! | ||||
| @ -1,32 +0,0 @@ | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-ssl-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/ingress.class: tectonic | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     nginx.ingress.kubernetes.io/proxy-body-size: "0" | ||||
|     ingress.kubernetes.io/ssl-redirect: "true" | ||||
|     # Replace letsencrypt-prod with the name of the certificate issuer | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-prod | ||||
|     #ingress.kubernetes.io/rewrite-target: "/" | ||||
|     #ingress.kubernetes.io/app-root: "/ui" | ||||
|     #ingress.kubernetes.io/follow-redirects: "true" | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/" | ||||
|         backend: | ||||
|           serviceName: front | ||||
|           servicePort: 80 | ||||
| @ -0,0 +1,86 @@ | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-admin-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     nginx.ingress.kubernetes.io/proxy-body-size: "0" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     ingress.kubernetes.io/permanent-redirect: "https://mail.example.com/admin/ui/" | ||||
|     ingress.kubernetes.io/follow-redirects: "true" | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/admin" | ||||
|         backend: | ||||
|           serviceName: admin | ||||
|           servicePort: 80 | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-admin-ui-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     ingress.kubernetes.io/rewrite-target: "/ui" | ||||
|     ingress.kubernetes.io/configuration-snippet: | | ||||
|       proxy_set_header X-Forwarded-Prefix /admin; | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/admin/ui" | ||||
|         backend: | ||||
|           serviceName: admin | ||||
|           servicePort: 80 | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-admin-static-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     ingress.kubernetes.io/rewrite-target: "/static" | ||||
|     ingress.kubernetes.io/configuration-snippet: | | ||||
|       proxy_set_header X-Forwarded-Prefix /admin; | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/admin/static" | ||||
|         backend: | ||||
|           serviceName: admin | ||||
|           servicePort: 80 | ||||
| @ -1,4 +1,3 @@ | ||||
| 
 | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
| @ -0,0 +1,30 @@ | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-antispam-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     ingress.kubernetes.io/configuration-snippet: | | ||||
|       rewrite ^/admin/antispam/(.*) /$1 break; | ||||
|       auth_request /internal/auth/admin; | ||||
|       proxy_set_header X-Real-IP ""; | ||||
|       proxy_set_header X-Forwarded-For ""; | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: frontend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/admin/antispam" | ||||
|         backend: | ||||
|           serviceName: antispam | ||||
|           servicePort: 11334 | ||||
| @ -0,0 +1,46 @@ | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-webdav-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     nginx.ingress.kubernetes.io/proxy-body-size: "0" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     #ingress.kubernetes.io/auth-url: http://admin.mailu-mailserver.svc.cluster.local/internal/auth/basic | ||||
|     ingress.kubernetes.io/configuration-snippet: | | ||||
|       rewrite ^/webdav/(.*) /$1 break; | ||||
|       auth_request /internal/auth/basic; | ||||
|       proxy_set_header Host $http_host; | ||||
|       proxy_set_header X-Real-IP $remote_addr; | ||||
|       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
|       auth_request_set $user $upstream_http_x_user; | ||||
|       proxy_set_header X-Remote-User $user; | ||||
|       proxy_set_header X-Script-Name /webdav; | ||||
|     ingress.kubernetes.io/server-snippet: | | ||||
|       location /internal { | ||||
|         internal; | ||||
| 
 | ||||
|         proxy_set_header Authorization $http_authorization; | ||||
|         proxy_pass_header Authorization; | ||||
|         proxy_pass http://admin.mailu-mailserver.svc.cluster.local; | ||||
|         proxy_pass_request_body off; | ||||
|         proxy_set_header Content-Length ""; | ||||
|       } | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: frontend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/webdav" | ||||
|         backend: | ||||
|           serviceName: webdav | ||||
|           servicePort: 5232 | ||||
| @ -0,0 +1,31 @@ | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-webmail-ingress | ||||
|   namespace: mailu-mailserver | ||||
|   annotations: | ||||
|     kubernetes.io/tls-acme: "true" | ||||
|     nginx.ingress.kubernetes.io/proxy-body-size: "0" | ||||
|     certmanager.k8s.io/cluster-issuer: letsencrypt-stage | ||||
|     nginx.ingress.kubernetes.io/configuration-snippet: | | ||||
|       proxy_set_header Host $http_host; | ||||
|       proxy_set_header X-Real-IP $remote_addr; | ||||
|       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
|       proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "webmail.example.com" | ||||
|     secretName: letsencrypt-webmail # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|     - host: "webmail.example.com" | ||||
|       http: | ||||
|         paths: | ||||
|         - path: "/" | ||||
|           backend: | ||||
|             serviceName: webmail | ||||
|             servicePort: 80 | ||||
| @ -1,26 +0,0 @@ | ||||
| Kubernetes setup | ||||
| ================ | ||||
| 
 | ||||
| Please note that Kubernetes setup is not yet well supported or documented, all | ||||
| tests currently run on Docker Compose. The configuration has not yet been updated | ||||
| to work properly with ngin authentication proxy. | ||||
| 
 | ||||
| Prepare the environment | ||||
| ----------------------- | ||||
| 
 | ||||
| The resource configurations in this folder assume that you have `Kubernetes Ingress`_ | ||||
| set up for your cluster. If you are not using the `NGINX Ingress Controller for Kubernetes`_, | ||||
| please ensure that the configuration specified in the file matches your set up. | ||||
| 
 | ||||
| .. _`Kubernetes Ingress`: https://kubernetes.io/docs/concepts/services-networking/ingress/ | ||||
| .. _`NGINX Ingress Controller for Kubernetes`: https://github.com/kubernetes/ingress/tree/master/controllers/nginx | ||||
| 
 | ||||
| Setup the Kubernetes service | ||||
| ---------------------------- | ||||
| 
 | ||||
| Using the resource configurations is simple: | ||||
| 
 | ||||
| 1. ``kubectl apply -f kubernetes-nginx-ingress-controller.yaml`` to configure an ingress controller with the proper settings. (If you have one set up already you may need to port the configuration to your own ingress). | ||||
| 2. ``kubectl apply -f kubernetes-mailu.yaml`` to create the resources required to run Mailu. | ||||
| 
 | ||||
| Based on the configuration, your Mailu instance should be available at ``mail.<hostname>.tld/admin`` (note that visiting just ``mail.<hostname>.tld`` will likely result in a 404 error). | ||||
| @ -1,419 +0,0 @@ | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: mailu-admin-ing | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - "mail.example.com" | ||||
|     secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt | ||||
|   rules: | ||||
|   - host: "mail.example.com" | ||||
|     http: | ||||
|       paths: | ||||
|       - path: "/admin" | ||||
|         backend: | ||||
|           serviceName: mailu-admin | ||||
|           servicePort: 80 | ||||
| 
 | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: mailu-redis | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mailu-redis | ||||
|         role: mail | ||||
|         tier: backend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: redis | ||||
|         image: redis:4.0-alpine | ||||
|         imagePullPolicy: Always | ||||
|         volumeMounts: | ||||
|           - mountPath: /data | ||||
|             name: redisdata | ||||
|         ports: | ||||
|           - containerPort: 6379 | ||||
|             name: redis | ||||
|             protocol: TCP | ||||
|       volumes: | ||||
|         - name: redisdata | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/redisdata | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: redis | ||||
|   labels: | ||||
|     app: mailu-redis | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   - name: redis | ||||
|     port: 6379 | ||||
|     protocol: TCP | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: mailu-imap | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mailu-imap | ||||
|         role: mail | ||||
|         tier: backend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: imap | ||||
|         image: mailu/dovecot:stable | ||||
|         imagePullPolicy: Always | ||||
|         env: | ||||
|           - name  : DOMAIN | ||||
|             value : example.com | ||||
|           - name  : HOSTNAME | ||||
|             value : mail.example.com | ||||
|           - name  : POSTMASTER | ||||
|             value : admin | ||||
|         volumeMounts: | ||||
|           - mountPath: /data | ||||
|             name: maildata | ||||
|           - mountPath: /mail | ||||
|             name: mailstate | ||||
|           - mountPath: /overrides | ||||
|             name: overrides | ||||
|           - mountPath: /certs | ||||
|             name: certs | ||||
|             readOnly: true | ||||
|         ports: | ||||
|           - containerPort: 2102 | ||||
|           - containerPort: 2525 | ||||
|           - containerPort: 143 | ||||
|           - containerPort: 993 | ||||
|           - containerPort: 4190 | ||||
|       volumes: | ||||
|         - name: maildata | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/maildata | ||||
|         - name: mailstate | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/mailstate | ||||
|         - name: overrides | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/overrides | ||||
|         - name: certs | ||||
|           secret: | ||||
|             items: | ||||
|               - key: tls.crt | ||||
|                 path: cert.pem | ||||
|               - key: tls.key | ||||
|                 path: key.pem | ||||
|             secretName: letsencrypt-certs-all | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: imap | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu-imap | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   ports: | ||||
|   - name: imap-auth | ||||
|     port: 2102 | ||||
|     protocol: TCP | ||||
|   - name: imap-transport | ||||
|     port: 2525 | ||||
|     protocol: TCP | ||||
|   - name: imap-default | ||||
|     port: 143 | ||||
|     protocol: TCP | ||||
|   - name: imap-ssl | ||||
|     port: 993 | ||||
|     protocol: TCP | ||||
|   - name: sieve | ||||
|     port: 4190 | ||||
|     protocol: TCP | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: mailu-smtp | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mailu-smtp | ||||
|         role: mail | ||||
|         tier: backend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: smtp | ||||
|         image: mailu/postfix:stable | ||||
|         imagePullPolicy: Always | ||||
|         env: | ||||
|           - name  : DOMAIN | ||||
|             value : example.com | ||||
|           - name  : HOSTNAME | ||||
|             value : mail.example.com | ||||
|           - name  : MESSAGE_SIZE_LIMIT | ||||
|             value : "50000000" | ||||
|           - name  : RELAYHOST | ||||
|             value : "" | ||||
|         volumeMounts: | ||||
|           - mountPath: /data | ||||
|             name: maildata | ||||
|           - mountPath: /overrides | ||||
|             name: overrides | ||||
|           - mountPath: /certs | ||||
|             name: certs | ||||
|             readOnly: true | ||||
|         ports: | ||||
|           - name: smtp | ||||
|             containerPort: 25 | ||||
|             protocol: TCP | ||||
|           - name: smtp-ssl | ||||
|             containerPort: 465 | ||||
|             protocol: TCP | ||||
|           - name: smtp-starttls | ||||
|             containerPort: 587 | ||||
|             protocol: TCP | ||||
|       volumes: | ||||
|         - name: maildata | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/maildata | ||||
|         - name: overrides | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/overrides | ||||
|         - name: certs | ||||
|           secret: | ||||
|             items: | ||||
|               - key: tls.crt | ||||
|                 path: cert.pem | ||||
|               - key: tls.key | ||||
|                 path: key.pem | ||||
|             secretName: letsencrypt-certs-all | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: smtp | ||||
|   labels: | ||||
|     app: mailu | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu-smtp | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   - name: smtp | ||||
|     port: 25 | ||||
|     protocol: TCP | ||||
|   - name: smtp-ssl | ||||
|     port: 465 | ||||
|     protocol: TCP | ||||
|   - name: smtp-starttls | ||||
|     port: 587 | ||||
|     protocol: TCP | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: mailu-security | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mailu-security | ||||
|         role: mail | ||||
|         tier: backend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: antispam | ||||
|         image: mailu/rspamd:stable | ||||
|         imagePullPolicy: Always | ||||
|         ports: | ||||
|           - name: antispam | ||||
|             containerPort: 11333 | ||||
|             protocol: TCP | ||||
|         volumeMounts: | ||||
|           - name: filter | ||||
|             mountPath: /var/lib/rspamd | ||||
|       - name: antivirus | ||||
|         image: mailu/clamav:stable | ||||
|         imagePullPolicy: Always | ||||
|         ports: | ||||
|           - name: antivirus | ||||
|             containerPort: 3310 | ||||
|             protocol: TCP | ||||
|         volumeMounts: | ||||
|           - name: filter | ||||
|             mountPath: /data | ||||
|       volumes: | ||||
|         - name: filter | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/filter | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: antispam | ||||
|   labels: | ||||
|     app: mailu-antispam | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu-security | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   - name: antispam | ||||
|     port: 11333 | ||||
|     protocol: TCP | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: antivirus | ||||
|   labels: | ||||
|     app: mailu-antivirus | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu-security | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   - name: antivirus | ||||
|     port: 3310 | ||||
|     protocol: TCP | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: mailu-admin | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mailu-admin | ||||
|         role: mail | ||||
|         tier: backend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: admin | ||||
|         image: mailu/admin:stable | ||||
|         imagePullPolicy: Always | ||||
|         env: | ||||
|           - name  : DOMAIN | ||||
|             value : example.com | ||||
|           - name  : HOSTNAME | ||||
|             value : mail.example.com | ||||
|           - name  : POSTMASTER | ||||
|             value : core | ||||
|           - name  : SECRET_KEY | ||||
|             value : pleasereplacethiswithabetterkey | ||||
|           - name  : DEBUG | ||||
|             value : "True" | ||||
|         volumeMounts: | ||||
|           - name: maildata | ||||
|             mountPath: /data | ||||
|           - name: dkim | ||||
|             mountPath: /dkim | ||||
|           - name: certs | ||||
|             mountPath: /certs | ||||
|             readOnly: true | ||||
|           # - name: docker | ||||
|           #   mountPath: /var/run/docker.sock | ||||
|           #   readOnly: true | ||||
|         ports: | ||||
|           - name: http | ||||
|             containerPort: 80 | ||||
|             protocol: TCP | ||||
|       volumes: | ||||
|         - name: maildata | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/maildata | ||||
|         - name: dkim | ||||
|           hostPath: | ||||
|             path: /var/data/mailu/dkim | ||||
|         - name: certs | ||||
|           secret: | ||||
|             items: | ||||
|               - key: tls.crt | ||||
|                 path: cert.pem | ||||
|               - key: tls.key | ||||
|                 path: key.pem | ||||
|             secretName: letsencrypt-certs-all | ||||
|         # - name: docker | ||||
|         #   hostPath: | ||||
|         #     path: /var/run/docker.sock | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: mailu-admin | ||||
|   labels: | ||||
|     app: mailu-admin | ||||
|     role: mail | ||||
|     tier: backend | ||||
| spec: | ||||
|   selector: | ||||
|     app: mailu-admin | ||||
|     role: mail | ||||
|     tier: backend | ||||
|   ports: | ||||
|   - name: http | ||||
|     port: 80 | ||||
|     protocol: TCP | ||||
| @ -1,84 +0,0 @@ | ||||
| --- | ||||
| kind: ConfigMap | ||||
| apiVersion: v1 | ||||
| metadata: | ||||
|   name: nginx-configuration | ||||
|   namespace: ingress-nginx | ||||
|   labels: | ||||
|     app: ingress-nginx | ||||
| 
 | ||||
| --- | ||||
| kind: ConfigMap | ||||
| apiVersion: v1 | ||||
| metadata: | ||||
|   name: udp-services | ||||
|   namespace: ingress-nginx | ||||
| 
 | ||||
| --- | ||||
| kind: ConfigMap | ||||
| apiVersion: v1 | ||||
| metadata: | ||||
|   name: tcp-services | ||||
|   namespace: ingress-nginx | ||||
| data: | ||||
|   25: "mailu/smtp:25" | ||||
|   465: "mailu/smtp:465" | ||||
|   587: "mailu/smtp:587" | ||||
|   143: "mailu/imap:143" | ||||
|   993: "mailu/imap:993" | ||||
| 
 | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: nginx-ingress-controller | ||||
|   namespace: kube-system | ||||
|   labels: | ||||
|     k8s-app: nginx-ingress-controller | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         k8s-app: nginx-ingress-controller | ||||
|       annotations: | ||||
|         prometheus.io/port: '10254' | ||||
|         prometheus.io/scrape: 'true' | ||||
|     spec: | ||||
|       # hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration | ||||
|       # however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host | ||||
|       # that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used | ||||
|       # like with kubeadm | ||||
|       # hostNetwork: true | ||||
|       terminationGracePeriodSeconds: 60 | ||||
|       containers: | ||||
|       - image: gcr.io/google_containers/nginx-ingress-controller:0.11.0 | ||||
|         name: nginx-ingress-controller | ||||
|         args: | ||||
|           - /nginx-ingress-controller | ||||
|           - --default-backend-service=$(POD_NAMESPACE)/default-http-backend | ||||
|           - --configmap=$(POD_NAMESPACE)/nginx-configuration | ||||
|           - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services | ||||
|           - --udp-services-configmap=$(POD_NAMESPACE)/udp-services | ||||
|           - --annotations-prefix=nginx.ingress.kubernetes.io | ||||
|         readinessProbe: | ||||
|           httpGet: | ||||
|             path: /healthz | ||||
|             port: 10254 | ||||
|             scheme: HTTP | ||||
|         livenessProbe: | ||||
|           httpGet: | ||||
|             path: /healthz | ||||
|             port: 10254 | ||||
|             scheme: HTTP | ||||
|           initialDelaySeconds: 10 | ||||
|           timeoutSeconds: 1 | ||||
|         env: | ||||
|           - name: POD_NAME | ||||
|             valueFrom: | ||||
|               fieldRef: | ||||
|                 fieldPath: metadata.name | ||||
|           - name: POD_NAMESPACE | ||||
|             valueFrom: | ||||
|               fieldRef: | ||||
|                 fieldPath: metadata.namespace | ||||
| @ -0,0 +1,252 @@ | ||||
| # Install Mailu on a docker swarm | ||||
| 
 | ||||
| ## Prequisites | ||||
| 
 | ||||
| ### Swarm | ||||
| 
 | ||||
| In order to deploy Mailu on a swarm, you will first need to initialize the swarm: | ||||
| 
 | ||||
| The main command will be: | ||||
| ```bash | ||||
| docker swarm init --advertise-addr <IP_ADDR> | ||||
| ``` | ||||
| See https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/ | ||||
| 
 | ||||
| If you want to add other managers or workers, please use: | ||||
| ```bash | ||||
| docker swarm join --token xxxxx  | ||||
| ``` | ||||
| See https://docs.docker.com/engine/swarm/join-nodes/ | ||||
| 
 | ||||
| You have now a working swarm, and you can check its status with: | ||||
| ```bash | ||||
| core@coreos-01 ~/git/Mailu/docs/swarm/1.5 $ docker node ls | ||||
| ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION | ||||
| xhgeekkrlttpmtgmapt5hyxrb     black-pearl         Ready               Active                                  18.06.0-ce | ||||
| sczlqjgfhehsfdjhfhhph1nvb *   coreos-01           Ready               Active              Leader              18.03.1-ce | ||||
| mzrm9nbdggsfz4sgq6dhs5i6n     flying-dutchman     Ready               Active                                  18.06.0-ce | ||||
| ``` | ||||
| 
 | ||||
| ### Volume definition | ||||
| For data persistance (the Mailu services might be launched/relaunched on any of the swarm nodes), we need to have Mailu data stored in a manner accessible by every manager or worker in the swarm. | ||||
| 
 | ||||
| Hereafter we will assume that "Mailu Data" is available on every node at "$ROOT/certs:/certs" (GlusterFS and nfs shares have been successfully used). | ||||
| 
 | ||||
| On this example, we are using: | ||||
| - the mesh routing mode (default mode). With this mode, each service is given a virtual IP adress and docker manages the routing between this virtual IP and the container(s) providing this service.  | ||||
| - the default ingress mode. | ||||
| 
 | ||||
| ### Allow authentification with the mesh routing | ||||
| In order to allow every (front & webmail) container to access the other services, we will use the variable POD_ADDRESS_RANGE. | ||||
| 
 | ||||
| Let's create the mailu_default network: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker network create -d overlay --attachable mailu_default | ||||
| core@coreos-01 ~ $ docker network inspect mailu_default | grep Subnet | ||||
|                     "Subnet": "10.0.1.0/24", | ||||
| ``` | ||||
| In the docker-compose.yml file, we will then use POD_ADDRESS_RANGE = 10.0.1.0/24  | ||||
| In fact, imap & smtp logs doesn't show the IPs from the front(s) container(s), but the IP of  "mailu_default-endpoint". So it is sufficient to set POD_ADDRESS_RANGE to this specific ip (which can be found by inspecting mailu_default network). The issue is that this endpoint is created while the stack is created, I did'nt figure a way to determine this IP before the stack creation... | ||||
| 
 | ||||
| ### Limitation with the ingress mode | ||||
| With the default ingress mode, the front(s) container(s) will see origin IP(s) all being 10.255.0.x (which is the ingress-endpoint, can be found by inspecting the ingress network) | ||||
| 
 | ||||
| This issue is known and discussed here: | ||||
| 
 | ||||
| https://github.com/moby/moby/issues/25526 | ||||
| 
 | ||||
| A workaround (using network host mode and global deployment) is discussed here: | ||||
| 
 | ||||
| https://github.com/moby/moby/issues/25526#issuecomment-336363408  | ||||
| 
 | ||||
| ### Don't create an open relay ! | ||||
| As a side effect of this ingress mode "feature", make sure that the ingress subnet is not in your RELAYHOST, otherwise you would create an smtp open relay :-( | ||||
| 
 | ||||
| 
 | ||||
| ## Scalability | ||||
| - smtp and imap are scalable | ||||
| - front and webmail are scalable (pending POD_ADDRESS_RANGE is used), although the let's encrypt magic might not like it (race condidtion ? or risk to be banned by let's encrypt server if too many front containers attemps to renew the certs at the same time)  | ||||
| - redis, antispam, antivirus, fetchmail, admin, webdav have not been tested (hence replicas=1 in the following docker-compose.yml file) | ||||
| 
 | ||||
| ## Variable substitution and docker-compose.yml | ||||
| The docker stack deploy command doesn't support variable substitution in the .yml file itself.  | ||||
| As a consequence, we cannot simply use ``` docker stack deploy -c docker.compose.yml mailu ``` | ||||
| Instead, we will use the following work-around: | ||||
| ``` echo "$(docker-compose -f /mnt/docker/apps/mailu/docker-compose.yml config 2>/dev/null)" | docker stack deploy -c- mailu ``` | ||||
| 
 | ||||
| We need also to: | ||||
| - add a deploy section for every service | ||||
| - modify the way the ports are defined for the front service | ||||
| - add the POD_ADDRESS_RANGE definition for imap, smtp and antispam services | ||||
| 
 | ||||
| ## Docker compose  | ||||
| An example of docker-compose-stack.yml file is available here: | ||||
| 
 | ||||
| ```yaml | ||||
| 
 | ||||
| version: '3.2' | ||||
| 
 | ||||
| services: | ||||
| 
 | ||||
|   front: | ||||
|     image: mailu/nginx:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     ports: | ||||
|       - target: 80 | ||||
|         published: 80 | ||||
|       - target: 443 | ||||
|         published: 443 | ||||
|       - target: 110 | ||||
|         published: 110 | ||||
|       - target: 143 | ||||
|         published: 143 | ||||
|       - target: 993 | ||||
|         published: 993 | ||||
|       - target: 995 | ||||
|         published: 995 | ||||
|       - target: 25 | ||||
|         published: 25 | ||||
|       - target: 465 | ||||
|         published: 465 | ||||
|       - target: 587 | ||||
|         published: 587 | ||||
|     volumes: | ||||
|       - "$ROOT/certs:/certs" | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   redis: | ||||
|     image: redis:alpine | ||||
|     restart: always | ||||
|     volumes: | ||||
|       - "$ROOT/redis:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   imap: | ||||
|     image: mailu/dovecot:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     volumes: | ||||
|       - "$ROOT/mail:/mail" | ||||
|       - "$ROOT/overrides:/overrides" | ||||
|     depends_on: | ||||
|       - front | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   smtp: | ||||
|     image: mailu/postfix:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     volumes: | ||||
|       - "$ROOT/overrides:/overrides" | ||||
|     depends_on: | ||||
|       - front | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   antispam: | ||||
|     image: mailu/rspamd:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     volumes: | ||||
|       - "$ROOT/filter:/var/lib/rspamd" | ||||
|       - "$ROOT/dkim:/dkim" | ||||
|       - "$ROOT/overrides/rspamd:/etc/rspamd/override.d" | ||||
|     depends_on: | ||||
|       - front | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   antivirus: | ||||
|     image: mailu/none:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|       - "$ROOT/filter:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   webdav: | ||||
|     image: mailu/none:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|       - "$ROOT/dav:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   admin: | ||||
|     image: mailu/admin:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|       - "$ROOT/data:/data" | ||||
|       - "$ROOT/dkim:/dkim" | ||||
|       - /var/run/docker.sock:/var/run/docker.sock:ro | ||||
|     depends_on: | ||||
|       - redis | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   webmail: | ||||
|     image: mailu/roundcube:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|       - "$ROOT/webmail:/data" | ||||
|     depends_on: | ||||
|       - imap | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   fetchmail: | ||||
|     image: mailu/fetchmail:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
| networks: | ||||
|   default: | ||||
|     external: | ||||
|       name: mailu_default | ||||
| ``` | ||||
| 
 | ||||
| ## Deploy Mailu on the docker swarm | ||||
| Run the following command: | ||||
| ```bash | ||||
| echo "$(docker-compose -f /mnt/docker/apps/mailu/docker-compose.yml config 2>/dev/null)" | docker stack deploy -c- mailu | ||||
| ``` | ||||
| See how the services are being deployed: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker service ls | ||||
| ID                  NAME                                 MODE                REPLICAS            IMAGE                                     PORTS | ||||
| ywnsetmtkb1l        mailu_antivirus                      replicated          1/1                 mailu/none:master | ||||
| pqokiaz0q128        mailu_fetchmail                      replicated          1/1                 mailu/fetchmail:master | ||||
| ``` | ||||
| check a specific service: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker service ps mailu_fetchmail | ||||
| ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE         ERROR               PORTS | ||||
| tbu8ppgsdffj        mailu_fetchmail.1   mailu/fetchmail:master   coreos-01           Running             Running 11 days ago | ||||
| ``` | ||||
| You might also have a look on the logs: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker service logs -f mailu_fetchmail | ||||
| ``` | ||||
| 
 | ||||
| ## Remove the stack | ||||
| Run the follwoing command: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker stack rm mailu | ||||
| ``` | ||||
| @ -0,0 +1,357 @@ | ||||
| # Install Mailu on a docker swarm | ||||
| 
 | ||||
| ## Prequisites | ||||
| 
 | ||||
| ### Swarm | ||||
| 
 | ||||
| In order to deploy Mailu on a swarm, you will first need to initialize the swarm: | ||||
| 
 | ||||
| The main command will be: | ||||
| ```bash | ||||
| docker swarm init --advertise-addr <IP_ADDR> | ||||
| ``` | ||||
| See https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/ | ||||
| 
 | ||||
| If you want to add other managers or workers, please use: | ||||
| ```bash | ||||
| docker swarm join --token xxxxx  | ||||
| ``` | ||||
| See https://docs.docker.com/engine/swarm/join-nodes/ | ||||
| 
 | ||||
| You have now a working swarm, and you can check its status with: | ||||
| ```bash | ||||
| core@coreos-01 ~/git/Mailu/docs/swarm/1.5 $ docker node ls | ||||
| ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION | ||||
| xhgeekkrlttpmtgmapt5hyxrb     black-pearl         Ready               Active                                  18.06.0-ce | ||||
| sczlqjgfhehsfdjhfhhph1nvb *   coreos-01           Ready               Active              Leader              18.03.1-ce | ||||
| mzrm9nbdggsfz4sgq6dhs5i6n     flying-dutchman     Ready               Active                                  18.06.0-ce | ||||
| ``` | ||||
| 
 | ||||
| ### Volume definition | ||||
| For data persistance (the Mailu services might be launched/relaunched on any of the swarm nodes), we need to have Mailu data stored in a manner accessible by every manager or worker in the swarm. | ||||
| Hereafter we will use a NFS share: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ showmount -e 192.168.0.30 | ||||
| Export list for 192.168.0.30: | ||||
| /mnt/Pool1/pv            192.168.0.0 | ||||
| ``` | ||||
| 
 | ||||
| on the nfs server, I am using the following /etc/exports | ||||
| ```bash | ||||
| $more /etc/exports | ||||
| /mnt/Pool1/pv -alldirs -mapall=root -network 192.168.0.0 -mask 255.255.255.0  | ||||
| ``` | ||||
| on the nfs server, I created the Mailu directory (in fact I copied a working Mailu set-up) | ||||
| ```bash | ||||
| $mkdir /mnt/Pool1/pv/mailu | ||||
| ``` | ||||
| 
 | ||||
| On your manager node, mount the nfs share to check that the share is available: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ sudo mount -t nfs 192.168.0.30:/mnt/Pool1/pv/mailu /mnt/local/ | ||||
| ``` | ||||
| If this is ok, you can umount it: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ sudo umount /mnt/local/ | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Networking mode | ||||
| On this example, we are using: | ||||
| - the mesh routing mode (default mode). With this mode, each service is given a virtual IP adress and docker manages the routing between this virtual IP and the container(s) providing this service.  | ||||
| - the default ingress mode. | ||||
| 
 | ||||
| ### Allow authentification with the mesh routing | ||||
| In order to allow every (front & webmail) container to access the other services, we will use the variable POD_ADDRESS_RANGE. | ||||
| 
 | ||||
| Let's create the mailu_default network: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker network create -d overlay --attachable mailu_default | ||||
| core@coreos-01 ~ $ docker network inspect mailu_default | grep Subnet | ||||
|                     "Subnet": "10.0.1.0/24", | ||||
| ``` | ||||
| In the docker-compose.yml file, we will then use POD_ADDRESS_RANGE = 10.0.1.0/24  | ||||
| In fact, imap & smtp logs doesn't show the IPs from the front(s) container(s), but the IP of  "mailu_default-endpoint". So it is sufficient to set POD_ADDRESS_RANGE to this specific ip (which can be found by inspecting mailu_default network). The issue is that this endpoint is created while the stack is created, I did'nt figure a way to determine this IP before the stack creation... | ||||
| 
 | ||||
| ### Limitation with the ingress mode | ||||
| With the default ingress mode, the front(s) container(s) will see origin IP(s) all being 10.255.0.x (which is the ingress-endpoint, can be found by inspecting the ingress network) | ||||
| 
 | ||||
| This issue is known and discussed here: | ||||
| 
 | ||||
| https://github.com/moby/moby/issues/25526 | ||||
| 
 | ||||
| A workaround (using network host mode and global deployment) is discussed here: | ||||
| 
 | ||||
| https://github.com/moby/moby/issues/25526#issuecomment-336363408  | ||||
| 
 | ||||
| ### Don't create an open relay ! | ||||
| As a side effect of this ingress mode "feature", make sure that the ingress subnet is not in your RELAYHOST, otherwise you would create an smtp open relay :-( | ||||
| 
 | ||||
| 
 | ||||
| ## Scalability | ||||
| - smtp and imap are scalable | ||||
| - front and webmail are scalable (pending POD_ADDRESS_RANGE is used), although the let's encrypt magic might not like it (race condidtion ? or risk to be banned by let's encrypt server if too many front containers attemps to renew the certs at the same time)  | ||||
| - redis, antispam, antivirus, fetchmail, admin, webdav have not been tested (hence replicas=1 in the following docker-compose.yml file) | ||||
| 
 | ||||
| ## Variable substitution and docker-compose.yml | ||||
| The docker stack deploy command doesn't support variable substitution in the .yml file itself. As a consequence, we need to use the following work-around: | ||||
| ``` echo "$(docker-compose -f /mnt/docker/apps/mailu/docker-compose.yml config 2>/dev/null)" | docker stack deploy -c- mailu ``` | ||||
| 
 | ||||
| We need also to: | ||||
| - change the way we define the volumes (nfs share in our case) | ||||
| - add a deploy section for every service | ||||
| - the way the ports are defined for the front service | ||||
| 
 | ||||
| ## Docker compose  | ||||
| An example of docker-compose-stack.yml file is available here: | ||||
| 
 | ||||
| ```yaml | ||||
| 
 | ||||
| version: '3.2' | ||||
| 
 | ||||
| services: | ||||
| 
 | ||||
|   front: | ||||
|     image: mailu/nginx:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     ports: | ||||
|       - target: 80 | ||||
|         published: 80 | ||||
|       - target: 443 | ||||
|         published: 443 | ||||
|       - target: 110 | ||||
|         published: 110 | ||||
|       - target: 143 | ||||
|         published: 143 | ||||
|       - target: 993 | ||||
|         published: 993 | ||||
|       - target: 995 | ||||
|         published: 995 | ||||
|       - target: 25 | ||||
|         published: 25 | ||||
|       - target: 465 | ||||
|         published: 465 | ||||
|       - target: 587 | ||||
|         published: 587 | ||||
|     volumes: | ||||
| #      - "$ROOT/certs:/certs" | ||||
|       - type: volume | ||||
|         source: mailu_certs | ||||
|         target: /certs | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   redis: | ||||
|     image: redis:alpine | ||||
|     restart: always | ||||
|     volumes: | ||||
| #      - "$ROOT/redis:/data" | ||||
|       - type: volume | ||||
|         source: mailu_redis | ||||
|         target: /data | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   imap: | ||||
|     image: mailu/dovecot:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     volumes: | ||||
| #      - "$ROOT/mail:/mail" | ||||
|       - type: volume | ||||
|         source: mailu_mail | ||||
|         target: /mail | ||||
| #      - "$ROOT/overrides:/overrides" | ||||
|       - type: volume | ||||
|         source: mailu_overrides | ||||
|         target: /overrides | ||||
|     depends_on: | ||||
|       - front | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   smtp: | ||||
|     image: mailu/postfix:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     volumes: | ||||
| #      - "$ROOT/overrides:/overrides" | ||||
|       - type: volume | ||||
|         source: mailu_overrides | ||||
|         target: /overrides | ||||
|     depends_on: | ||||
|       - front | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   antispam: | ||||
|     image: mailu/rspamd:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE=10.0.1.0/24 | ||||
|     depends_on: | ||||
|       - front | ||||
|     volumes: | ||||
| #      - "$ROOT/filter:/var/lib/rspamd" | ||||
|       - type: volume | ||||
|         source: mailu_filter | ||||
|         target: /var/lib/rspamd | ||||
| #      - "$ROOT/dkim:/dkim" | ||||
|       - type: volume | ||||
|         source: mailu_dkim | ||||
|         target: /dkim | ||||
| #      - "$ROOT/overrides/rspamd:/etc/rspamd/override.d" | ||||
|       - type: volume | ||||
|         source: mailu_overrides_rspamd | ||||
|         target: /etc/rspamd/override.d | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   antivirus: | ||||
|     image: mailu/none:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
| #      - "$ROOT/filter:/data" | ||||
|       - type: volume | ||||
|         source: mailu_filter | ||||
|         target: /data | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   webdav: | ||||
|     image: mailu/none:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
| #      - "$ROOT/dav:/data" | ||||
|       - type: volume | ||||
|         source: mailu_dav | ||||
|         target: /data | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   admin: | ||||
|     image: mailu/admin:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
| #      - "$ROOT/data:/data" | ||||
|       - type: volume | ||||
|         source: mailu_data | ||||
|         target: /data | ||||
| #      - "$ROOT/dkim:/dkim" | ||||
|       - type: volume | ||||
|         source: mailu_dkim | ||||
|         target: /dkim | ||||
|       - /var/run/docker.sock:/var/run/docker.sock:ro | ||||
|     depends_on: | ||||
|       - redis | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   webmail: | ||||
|     image: mailu/roundcube:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
| #      - "$ROOT/webmail:/data" | ||||
|       - type: volume | ||||
|         source: mailu_data | ||||
|         target: /data | ||||
|     depends_on: | ||||
|       - imap | ||||
|     deploy: | ||||
|       replicas: 2 | ||||
| 
 | ||||
|   fetchmail: | ||||
|     image: mailu/fetchmail:$VERSION | ||||
|     restart: always | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
| networks: | ||||
|   default: | ||||
|     external: | ||||
|       name: mailu_default | ||||
| 
 | ||||
| volumes: | ||||
|   mailu_filter: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/filter" | ||||
|   mailu_dkim: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/dkim" | ||||
|   mailu_overrides_rspamd: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/overrides/rspamd" | ||||
|   mailu_data: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/data" | ||||
|   mailu_mail: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/mail" | ||||
|   mailu_overrides: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/overrides" | ||||
|   mailu_dav: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/dav" | ||||
|   mailu_certs: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/certs" | ||||
|   mailu_redis: | ||||
|     driver_opts: | ||||
|       type: "nfs" | ||||
|       o: "addr=192.168.0.30,soft,rw" | ||||
|       device: ":/mnt/Pool1/pv/mailu/redis" | ||||
| ``` | ||||
| 
 | ||||
| ## Deploy Mailu on the docker swarm | ||||
| Run the following command: | ||||
| ```bash | ||||
| echo "$(docker-compose -f /mnt/docker/apps/mailu/docker-compose.yml config 2>/dev/null)" | docker stack deploy -c- mailu | ||||
| ``` | ||||
| See how the services are being deployed: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker service ls | ||||
| ID                  NAME                                 MODE                REPLICAS            IMAGE                                     PORTS | ||||
| ywnsetmtkb1l        mailu_antivirus                      replicated          1/1                 mailu/none:master | ||||
| pqokiaz0q128        mailu_fetchmail                      replicated          1/1                 mailu/fetchmail:master | ||||
| ``` | ||||
| check a specific service: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker service ps mailu_fetchmail | ||||
| ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE         ERROR               PORTS | ||||
| tbu8ppgsdffj        mailu_fetchmail.1   mailu/fetchmail:master   coreos-01           Running             Running 11 days ago | ||||
| ``` | ||||
| 
 | ||||
| ## Remove the stack | ||||
| Run the follwoing command: | ||||
| ```bash | ||||
| core@coreos-01 ~ $ docker stack rm mailu | ||||
| ``` | ||||
| @ -0,0 +1,34 @@ | ||||
| rule "local" { | ||||
|     # Fuzzy storage server list | ||||
|     servers = "localhost:11335"; | ||||
|     # Default symbol for unknown flags | ||||
|     symbol = "LOCAL_FUZZY_UNKNOWN"; | ||||
|     # Additional mime types to store/check | ||||
|     mime_types = ["application/*"]; | ||||
|     # Hash weight threshold for all maps | ||||
|     max_score = 20.0; | ||||
|     # Whether we can learn this storage | ||||
|     read_only = no; | ||||
|     # Ignore unknown flags | ||||
|     skip_unknown = yes; | ||||
|     # Hash generation algorithm | ||||
|     algorithm = "mumhash"; | ||||
| 
 | ||||
|     # Map flags to symbols | ||||
|     fuzzy_map = { | ||||
|         LOCAL_FUZZY_DENIED { | ||||
|             # Local threshold | ||||
|             max_score = 20.0; | ||||
|             # Flag to match | ||||
|             flag = 11; | ||||
|         } | ||||
|         LOCAL_FUZZY_PROB { | ||||
|             max_score = 10.0; | ||||
|             flag = 12; | ||||
|         } | ||||
|         LOCAL_FUZZY_WHITE { | ||||
|             max_score = 2.0; | ||||
|             flag = 13; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| group "fuzzy" { | ||||
|     max_score = 12.0; | ||||
|     symbol "LOCAL_FUZZY_UNKNOWN" { | ||||
|         weight = 5.0; | ||||
|         description = "Generic fuzzy hash match"; | ||||
|     } | ||||
|     symbol "LOCAL_FUZZY_DENIED" { | ||||
|         weight = 12.0; | ||||
|         description = "Denied fuzzy hash"; | ||||
|     } | ||||
|     symbol "LOCAL_FUZZY_PROB" { | ||||
|         weight = 5.0; | ||||
|         description = "Probable fuzzy hash"; | ||||
|     } | ||||
|     symbol "LOCAL_FUZZY_WHITE" { | ||||
|         weight = -2.1; | ||||
|         description = "Whitelisted fuzzy hash"; | ||||
|     } | ||||
| } | ||||
| @ -1,3 +1,4 @@ | ||||
| type = "controller"; | ||||
| bind_socket = "*:11334"; | ||||
| password = "mailu"; | ||||
| secure_ip = "{{ FRONT_ADDRESS }}"; | ||||
| secure_ip = "{% if POD_ADDRESS_RANGE %}{{ POD_ADDRESS_RANGE }}{% else %}{{ FRONT_ADDRESS }}{% endif %}"; | ||||
|  | ||||
| @ -0,0 +1,6 @@ | ||||
| type = "fuzzy"; | ||||
| bind_socket = "*:11335"; | ||||
| count = 1; | ||||
| backend = "redis"; | ||||
| expire = 90d; | ||||
| allow_update = ["127.0.0.1"]; | ||||
| @ -1 +1,2 @@ | ||||
| type = "normal"; | ||||
| enabled = false; | ||||
|  | ||||
| @ -1,124 +1,106 @@ | ||||
| {% set env='mailu.env' %} | ||||
| # This file is auto-generated by the Mailu configuration wizard. | ||||
| # Please read the documentation before attempting any change. | ||||
| # Generated for {{ flavor }} flavor | ||||
| 
 | ||||
| version: '2' | ||||
| version: '3.6' | ||||
| 
 | ||||
| services: | ||||
| 
 | ||||
|   # External dependencies | ||||
|   redis: | ||||
|     image: redis:alpine | ||||
|     restart: always | ||||
|     volumes: | ||||
|       - "$ROOT/redis:/data" | ||||
|       - "{{ root }}/redis:/data" | ||||
| 
 | ||||
|   # Core services | ||||
|   front: | ||||
|     image: mailu/nginx:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     env: | ||||
|       - TLS_FLAVOR={{ tls_flavor or 'letsencrypt' }} | ||||
|       - ADMIN={{ expose_admin or 'no' }} | ||||
|     ports: | ||||
|     {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} | ||||
|     {% if bind4 %} | ||||
|       - "$PUBLIC_IPV4:{{ port }}:{{ port }}" | ||||
|       - "{{ bind4 }}:{{ port }}:{{ port }}" | ||||
|     {% endif %} | ||||
|     {% if bind6 %} | ||||
|       - "$PUBLIC_IPV6:{{ port }}:{{ port }}" | ||||
|       - "{{ bind6 }}:{{ port }}:{{ port }}" | ||||
|     {% endif %} | ||||
|     {% endfor %} | ||||
|     {% if flavor in ('cert', 'mail') %} | ||||
|     volumes: | ||||
|       - "$ROOT/certs:/certs" | ||||
|     {% endif %} | ||||
|       - "{{ root }}/certs:/certs" | ||||
| 
 | ||||
|   admin: | ||||
|     image: mailu/admin:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     {% if not expose_admin %} | ||||
|     {% if not admin_enabled %} | ||||
|     ports: | ||||
|       - 127.0.0.1:8080:80 | ||||
|     {% endif %} | ||||
|     volumes: | ||||
|       - "$ROOT/data:/data" | ||||
|       - "$ROOT/dkim:/dkim" | ||||
|       - "{{ root }}/data:/data" | ||||
|       - "{{ root }}/dkim:/dkim" | ||||
|     depends_on: | ||||
|       - redis | ||||
| 
 | ||||
|   imap: | ||||
|     image: mailu/dovecot:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/data:/data" | ||||
|       - "$ROOT/mail:/mail" | ||||
|       - "$ROOT/overrides:/overrides" | ||||
|       - "{{ root }}/mail:/mail" | ||||
|       - "{{ root }}/overrides:/overrides" | ||||
|     depends_on: | ||||
|       - front | ||||
| 
 | ||||
|   smtp: | ||||
|     image: mailu/postfix:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/data:/data" | ||||
|       - "$ROOT/overrides:/overrides" | ||||
|       - "{{ root }}/overrides:/overrides" | ||||
|     depends_on: | ||||
|       - front | ||||
| 
 | ||||
|   # Optional services | ||||
|   {% if enable_antispam %} | ||||
|   {% if antispam_enabled %} | ||||
|   antispam: | ||||
|     image: mailu/rspamd:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/filter:/var/lib/rspamd" | ||||
|       - "$ROOT/dkim:/dkim" | ||||
|       - "$ROOT/overrides/rspamd:/etc/rspamd/override.d" | ||||
|       - "{{ root }}/filter:/var/lib/rspamd" | ||||
|       - "{{ root }}/dkim:/dkim" | ||||
|       - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d" | ||||
|     depends_on: | ||||
|       - front | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if enable_antivirus %} | ||||
|   {% if antivirus_enabled %} | ||||
|   antivirus: | ||||
|     image: mailu/clamav:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/filter:/data" | ||||
|       - "{{ root }}/filter:/data" | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if enable_webdav %} | ||||
|   {% if webdav_enabled %} | ||||
|   webdav: | ||||
|     image: mailu/radivale:{{ version }} | ||||
|     restart: always | ||||
|     image: mailu/radicale:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/dav:/data" | ||||
|       - "{{ root }}/dav:/data" | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if enable_fetchmail %} | ||||
|   {% if fetchmail_enabled %} | ||||
|   fetchmail: | ||||
|     image: mailu/fetchmail:{{ version }} | ||||
|     restart: always | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/data:/data" | ||||
|   {% endif %} | ||||
| 
 | ||||
|   # Webmail | ||||
|   {% if enable_webmail %} | ||||
|   {% if webmail_type != 'none' %} | ||||
|   webmail: | ||||
|     image: mailu/{{ webmail }}:{{ version }} | ||||
|     restart: always | ||||
|     image: mailu/{{ webmail_type }}:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "$ROOT/webmail:/data" | ||||
|       - "{{ root }}/webmail:/data" | ||||
|     depends_on: | ||||
|       - imap | ||||
|   {% endif %} | ||||
|  | ||||
| @ -0,0 +1,128 @@ | ||||
| {% set env='mailu.env' %} | ||||
| # This file is auto-generated by the Mailu configuration wizard. | ||||
| # Please read the documentation before attempting any change. | ||||
| # Generated for {{ flavor }} flavor | ||||
| 
 | ||||
| version: '3.6' | ||||
| 
 | ||||
| services: | ||||
| 
 | ||||
| # External dependencies | ||||
|   redis: | ||||
|     image: redis:alpine | ||||
|     restart: always | ||||
|     volumes: | ||||
|       - "{{ root }}/redis:/data" | ||||
| 
 | ||||
| # Core services | ||||
|   front: | ||||
|     image: mailu/nginx:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     ports: | ||||
|     {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} | ||||
|       - target: {{ port }} | ||||
|         published: {{ port }} | ||||
|         mode: overlay | ||||
|     {% endfor %} | ||||
|     volumes: | ||||
|       - "{{ root }}/certs:/certs" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   admin: | ||||
|     image: mailu/admin:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     {% if not admin_enabled %} | ||||
|     ports: | ||||
|       - 127.0.0.1:8080:80 | ||||
|     {% endif %} | ||||
|     volumes: | ||||
|       - "{{ root }}/data:/data" | ||||
|       - "{{ root }}/dkim:/dkim" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   imap: | ||||
|     image: mailu/dovecot:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     environment: | ||||
|     # Default to 10.0.1.0/24 | ||||
|       - POD_ADDRESS_RANGE={{ subnet }} | ||||
|     volumes: | ||||
|       - "{{ root }}/mail:/mail" | ||||
|       - "{{ root }}/overrides:/overrides" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   smtp: | ||||
|     image: mailu/postfix:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE={{ subnet }} | ||||
|     volumes: | ||||
|       - "{{ root }}/overrides:/overrides" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
| 
 | ||||
|   # Optional services | ||||
|   {% if antispam_enabled %} | ||||
|   antispam: | ||||
|     image: mailu/rspamd:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     environment: | ||||
|       - POD_ADDRESS_RANGE={{ subnet }} | ||||
|     volumes: | ||||
|       - "{{ root }}/filter:/var/lib/rspamd" | ||||
|       - "{{ root }}/dkim:/dkim" | ||||
|       - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if antivirus_enabled %} | ||||
|   antivirus: | ||||
|     image: mailu/clamav:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "{{ root }}/filter:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if webdav_enabled %} | ||||
|   webdav: | ||||
|     image: mailu/none:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "{{ root }}/dav:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if fetchmail_enabled %} | ||||
|   fetchmail: | ||||
|     image: mailu/fetchmail:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "{{ root }}/data:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
|   {% endif %} | ||||
| 
 | ||||
|   {% if webmail_type != 'none' %} | ||||
|   webmail: | ||||
|     image: mailu/roundcube:{{ version }} | ||||
|     env_file: {{ env }} | ||||
|     volumes: | ||||
|       - "{{ root }}/webmail:/data" | ||||
|     deploy: | ||||
|       replicas: 1 | ||||
|   {% endif %} | ||||
| 
 | ||||
| networks: | ||||
|   default: | ||||
|     driver: overlay | ||||
|     ipam: | ||||
|       driver: default | ||||
|       config: | ||||
|         - subnet: {{ subnet }} | ||||
| @ -0,0 +1 @@ | ||||
| ../compose/mailu.env | ||||
| @ -0,0 +1,60 @@ | ||||
| {% import "macros.html" as macros %} | ||||
| 
 | ||||
| {% call macros.panel("info", "Step 1 - Download your configuration files") %} | ||||
| <p>Docker Stack expects a project file, named <code>docker-compose.yml</code> | ||||
| in a project directory. First create your project directory.</p> | ||||
| 
 | ||||
| <pre><code>mkdir -p /{{ root }}/{redis,certs,data,dkim,mail,overrides/rspamd,filter,dav,webmail} | ||||
| </pre></code> | ||||
| 
 | ||||
| <p>Then download the project file. A side configuration file makes it easier | ||||
| to read and check the configuration variables generated by the wizard.</p> | ||||
| 
 | ||||
| <pre><code>cd {{ root }} | ||||
| curl {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }} > docker-compose.yml | ||||
| curl {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }} > mailu.env | ||||
| </pre></code> | ||||
| {% endcall %} | ||||
| 
 | ||||
| 
 | ||||
| {% call macros.panel("info", "Step 2 - Review the configuration") %} | ||||
| <p>We did not insert any malicious code on purpose in the configurations we | ||||
| distribute, but your download could have been intercepted, or our wizard | ||||
| website could have been compromised, so make sure you check the configuration | ||||
| files before going any further.</p> | ||||
| 
 | ||||
| <p>When you are done checking them, check them one last time.</p> | ||||
| {% endcall %} | ||||
| 
 | ||||
| {% call macros.panel("info", "Step 3 - Deploy docker stack") %} | ||||
| <p>To deploy the docker stack use the following commands. For more information about setting up docker swarm nodes read the | ||||
| 	<a href="https://docs.docker.com/get-started">docker documentation</a></p> | ||||
| 
 | ||||
| <pre><code>cd {{ root }} | ||||
| docker swarm init | ||||
| docker stack deploy -c docker-compose.yml mailu | ||||
| </pre></code> | ||||
| 
 | ||||
| In the docker stack deploy command, mailu is the app name. Feel free to change it.<br/> | ||||
| In order to display the running container you can use<br/> | ||||
| <pre><code>docker ps</code></pre> | ||||
| or | ||||
| <pre><code>docker stack ps --no-trunc mailu</code></pre> | ||||
| Command for removing docker stack is | ||||
| <pre><code>docker stack rm mailu</code></pre> | ||||
| 
 | ||||
| Before you can use Mailu, you must create the primary administrator user account. This should be {{ postmaster }}@{{ domain }}. Use the following command, changing PASSWORD to your liking: | ||||
| 
 | ||||
| <pre><code>docker exec $(docker ps | grep admin | cut -d ' ' -f1) python manage.py admin {{ postmaster }} {{ domain }} PASSWORD  | ||||
| </pre></code> | ||||
| 
 | ||||
| <p>Login to the admin interface to change the password for a safe one, at | ||||
| {% if admin_enabled %} | ||||
| one of the hostnames  | ||||
| <a href="https://{{ hostnames.split(',')[0] }}{{ admin_path }}">{{ hostnames.split(',')[0] }}{{ admin_path }}</a>. | ||||
| {% else %} | ||||
| <a href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> (only directly from the host running docker). | ||||
| {% endif %} | ||||
| And choose the "Update password" option in the left menu. | ||||
| </p> | ||||
| {% endcall %} | ||||
| @ -0,0 +1,39 @@ | ||||
| {% call macros.panel("info", "Step 4 - expose Mailu to the world") %} | ||||
| <p>A mail server must be exposed to the world to receive emails, send emails, | ||||
| and let users access their mailboxes. Mailu has some flexibility in the way | ||||
| you expose it to the world.</p> | ||||
| 
 | ||||
| <p>Among Mailu services, the <em>front</em> server is the one accepting connections, | ||||
| be it directly from the outside world, through a reverse proxy or in any | ||||
| complex configuration that you might want to setup. It needs to listen on some | ||||
| IP addresses in order to expose its public services. You must at least setup | ||||
| an IPv4 or an IPv6 address if you wish to access Mailu.</p> | ||||
| 
 | ||||
| <p><span class="label label-warning">Warning</span> You must use specific addresses, please | ||||
| avoid generic all-interfaces addresses like <code>0.0.0.0</code> or <code>::</code>.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>IPv4 listen address</label> | ||||
| <!--   Validates IPv4 address --> | ||||
|   <input class="form-control" type="text" name="bind4" value="127.0.0.1"  | ||||
|   		pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>IPv6 listen address</label> | ||||
| <!--   Validates IPv6 address --> | ||||
|   <input class="form-control" type="text" name="bind6" value="::1" | ||||
|   		pattern="^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"> | ||||
| </div> | ||||
| 
 | ||||
| <p>You server will be available under a main hostname but may expose multiple public | ||||
| hostnames. Every e-mail domain that points to this server must have one of the | ||||
| hostnames in its <code>MX</code> record. Hostnames must be coma-separated.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Public hostnames</label> | ||||
| <!--   Validates hostname or list of hostnames --> | ||||
|   <input class="form-control" type="text" name="hostnames" placeholder="my.host.name,other.host.name" multiple required | ||||
|     	pattern="^(?:(?:\w+(?:-+\w+)*\.)+[a-z]+)*(?:,(?:(?:\w+(?:-+\w+)*\.)+[a-z]+)\s*)*$"> | ||||
| </div> | ||||
| {% endcall %} | ||||
| @ -0,0 +1,78 @@ | ||||
| {% call macros.panel("info", "Step 2 - Initial configuration") %} | ||||
| <p>Before starting some variables must be set</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Root path: </label> | ||||
| <!--   Validates path --> | ||||
|   <input class="form-control" type="text" name="root" value="/mailu" required pattern="^/[-_A-Za-z0-9]+(/[-_A-Za-z0-9]*)*"> | ||||
| </div> | ||||
| 
 | ||||
| <p>In the next sections we need to set the postmaster address. This is a combination from the <i>postmaster</i> local part and the <i>main mail domain</i>. | ||||
| The <i>main mail domain</i> is also used as </i>"server display name"</i>. This is the way the SMTP server identifies himself when connecting to others. | ||||
| The Postmaster will get an e-mail address <postmaster>@<main_domain>. This address will receive the DMARC "rua" and "ruf" reports. | ||||
| Or in plain english: if receivers start to classify your mail as spam, this postmaster will be informed.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label> | ||||
|     Main mail domain and server display name. | ||||
|   </label> | ||||
| <!--   Validates domain name --> | ||||
|   <input class="form-control" type="text" name="domain" placeholder="e.g. mailu.io"  | ||||
|   		required pattern="^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$"> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Postmaster local part</label> | ||||
|   <input class="form-control" type="text" name="postmaster" value="admin" required> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Choose how you wish to handle security (TLS) certificates</label> | ||||
|   <br/> | ||||
|   <select class="btn btn-primary dropdown-toggle" name="tls_flavor"> | ||||
|   	{% for tlsflavor in ["letsencrypt", "cert", "notls", "mail", "mail-letsencrypt"] %} | ||||
|   	<option value="{{ tlsflavor }}" >{{ tlsflavor }}</option> | ||||
|   	{% endfor %} | ||||
|   </select> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Authentication rate limit (per source IP address)</label> | ||||
| <!--   Validates number input only --> | ||||
|   <p><input class="form-control" style="width: 7%; display: inline;" type="number" name="auth_ratelimit_pm"  | ||||
|   		value="10" required >/minute; | ||||
|   <input class="form-control" style="width: 7%; display: inline;;" type="number" name="auth_ratelimit_ph" | ||||
|   		value="1000" required >/hour</p> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-check form-check-inline"> | ||||
|   <label class="form-check-label"> | ||||
|     <input class="form-check-input" type="checkbox" name="disable_statistics" value="True"> | ||||
|     Opt-out of statistics | ||||
|   </label> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Website name</label> | ||||
|   <input class="form-control" type="text" name="site_name" value="Mailu" required> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Linked Website URL</label> | ||||
| <!--   Validates url with or without https:// --> | ||||
|   <input class="form-control" type="url" name="website" value="https://mailu.io" required | ||||
|   		pattern="^(https?://)?([a-zA-Z0-9]([a-zA-ZäöüÄÖÜ0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$"> | ||||
| </div> | ||||
| 
 | ||||
| <p>The admin interface is the main Mailu-specific bit, it provides tools to | ||||
| manage your email domains, users, etc.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Enable the admin UI (and path to the admin UI)</label> | ||||
|   <div class="input-group"> | ||||
|     <div class="input-group-addon"><input type="checkbox" name="admin_enabled" value="true"></div> | ||||
|     <input class="form-control" type="text" name="admin_path" value="/admin"> | ||||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| {% endcall %} | ||||
| @ -1,33 +0,0 @@ | ||||
| {% call macros.panel("info", "Step 2 - expose Mailu to the world") %} | ||||
| <p>A mail server must be exposed to the world to receive emails, send emails, | ||||
| and let users access their mailboxes. Mailu has some flexibility in the way | ||||
| you expose it to the world.</p> | ||||
| 
 | ||||
| <p>Among Mailu services, the <em>front</em> server is the one accepting connections, | ||||
| be it directly from the outside world, through a reverse proxy or in any | ||||
| complex configuration that you might want to setup. It needs to listen on some | ||||
| IP addresses in order to expose its public services. You must at least setup | ||||
| an IPv4 or an IPv6 address if you wish to access Mailu.</p> | ||||
| 
 | ||||
| <p><span class="label label-warning">Warning</span> You must use specific addresses, please | ||||
| avoid generic all-interfaces addresses like <code>0.0.0.0</code> or <code>::</code>.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>IPv4 listen address</label> | ||||
|   <input class="form-control" type="text" name="ip4" placeholder="1.2.3.4"> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>IPv6 listen address</label> | ||||
|   <input class="form-control" type="text" name="ip6" placeholder="2001:be4:1234::1"> | ||||
| </div> | ||||
| 
 | ||||
| <p>You server will be available under a main hostname but may expose multiple public | ||||
| hostnames. Every e-mail domain that points to this server must have one of the | ||||
| hostnames in its <code>MX</code> record. Hostnames must be coma-separated.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Public hostnames</label> | ||||
|   <input class="form-control" type="text" name="hostnames" placeholder="my.host.name,other.host.name" multiple> | ||||
| </div> | ||||
| {% endcall %} | ||||
| @ -1,16 +0,0 @@ | ||||
| {% call macros.panel("info", "Step 4 - enable optional features") %} | ||||
| <p>Mailu also comes with less common optional features that you might wish | ||||
| to enable.</p> | ||||
| 
 | ||||
| <p>The DAV service enables contacts and calendar storage through Mailu, | ||||
| it is especially userful when synchronizing your desktop and mobile devices.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Enable the DAV service (and path to the DAV service)</label> | ||||
|   <div class="input-group"> | ||||
|     <div class="input-group-addon"><input type="checkbox" name="dav_enabled" checked></div> | ||||
|     <input class="form-control" type="text" name="admin_path" value="/webdav"> | ||||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| {% endcall %} | ||||
| @ -0,0 +1,62 @@ | ||||
| {% call macros.panel("info", "Step 3 - pick some features") %} | ||||
| <p>Mailu comes with multiple base features, including a specific admin | ||||
| interface, Web email clients (webmails), antispam, antivirus, etc. If you | ||||
| wish to disable some of these features, you are free to do so.</p> | ||||
| 
 | ||||
| <p>Emails will be available through IMAP and POP3. You may also enable a Web | ||||
| email client. These do add some complexity but provide an easier way of | ||||
| accessing messages for beginner users.</p> | ||||
| 
 | ||||
| <!-- 	Switched from radio buttons to dropdown menu in order to remove the checkbox --> | ||||
| <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"> | ||||
|   	{% for webmailtype in ["none", "roundcube", "rainloop"] %} | ||||
|   	<option value="{{ webmailtype }}" >{{ webmailtype }}</option> | ||||
|   	{% endfor %} | ||||
|   </select> | ||||
|   <p></p> | ||||
|   <div class="input-group"> | ||||
| <!--     <div class="input-group-addon"><input type="checkbox" name="webmail_enabled" value="true"></div> --> | ||||
|     <input class="form-control" type="text" name="webmail_path" value="/webmail"> | ||||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| <p>Email filtering is a really important features. You can still disable it, which | ||||
| will prevent Mailu from doing spam filtering, virus filtering, and from applying | ||||
| white and blacklists that you may configure in the admin interface. You may | ||||
| also disable the antivirus if required (it does use aroung 1GB of ram).</p> | ||||
| 
 | ||||
| <div class="form-check form-check-inline"> | ||||
|   <label class="form-check-label"> | ||||
|     <input class="form-check-input" type="checkbox" name="antispam_enabled" value="rspamd" checked> | ||||
|     Enable the spam filtering service | ||||
|   </label> | ||||
| </div> | ||||
| <div class="form-check form-check-inline"> | ||||
|   <label class="form-check-label"> | ||||
|     <input class="form-check-input" type="checkbox" name="antivirus_enabled" value="clamav"> | ||||
|     Enable the antivirus service | ||||
|   </label> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-check form-check-inline"> | ||||
|   <label class="form-check-label"> | ||||
|     <input class="form-check-input" type="checkbox" name="webdav_enabled" value="radicale"> | ||||
|     Enable the webdav service | ||||
|   </label> | ||||
| </div> | ||||
| 
 | ||||
| <div class="form-check form-check-inline"> | ||||
|   <label class="form-check-label"> | ||||
|     <input class="form-check-input" type="checkbox" name="fetchmail_enabled" value="true"> | ||||
|     Enable fetchmail | ||||
|   </label> | ||||
| </div> | ||||
| 
 | ||||
| {% endcall %} | ||||
| @ -0,0 +1,21 @@ | ||||
| {% call macros.panel("info", "Step 4 - expose Mailu to the world") %} | ||||
| <p>A mail server must be exposed to the world to receive emails, send emails, | ||||
| and let users access their mailboxes. Mailu has some flexibility in the way | ||||
| you expose it to the world.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Subnet</label> | ||||
|   <input class="form-control" type="text" name="subnet" required pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))$"> | ||||
| </div> | ||||
| 
 | ||||
| <p>You server will be available under a main hostname but may expose multiple public | ||||
| hostnames. Every e-mail domain that points to this server must have one of the | ||||
| hostnames in its <code>MX</code> record. Hostnames must be coma-separated.</p> | ||||
| 
 | ||||
| <div class="form-group"> | ||||
|   <label>Public hostnames</label> | ||||
| <!--   Validates hostname or list of hostnames --> | ||||
|   <input class="form-control" type="text" name="hostnames" placeholder="my.host.name,other.host.name" multiple required | ||||
|     	pattern="^(?:(?:\w+(?:-+\w+)*\.)+[a-z]+)*(?:,(?:(?:\w+(?:-+\w+)*\.)+[a-z]+)\s*)*$"> | ||||
| </div> | ||||
| {% endcall %} | ||||
					Loading…
					
					
				
		Reference in New Issue
	
	 Tim Möhlmann
						Tim Möhlmann