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"; | require "vnd.dovecot.execute"; | ||||||
| 
 | 
 | ||||||
| execute :pipe "mailtrain" "spam"; | execute :pipe "spam"; | ||||||
|  | |||||||
| @ -1,2 +1,9 @@ | |||||||
| {% set version=github_version %} |  | ||||||
| {% extends "!layout.html" %} | {% 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 | apiVersion: extensions/v1beta1 | ||||||
| kind: Deployment | kind: Deployment | ||||||
| metadata: | 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"; | bind_socket = "*:11334"; | ||||||
| password = "mailu"; | 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; | enabled = false; | ||||||
|  | |||||||
| @ -1,124 +1,106 @@ | |||||||
| {% set env='mailu.env' %} | {% set env='mailu.env' %} | ||||||
| # This file is auto-generated by the Mailu configuration wizard. | # This file is auto-generated by the Mailu configuration wizard. | ||||||
| # Please read the documentation before attempting any change. | # Please read the documentation before attempting any change. | ||||||
|  | # Generated for {{ flavor }} flavor | ||||||
| 
 | 
 | ||||||
| version: '2' | version: '3.6' | ||||||
| 
 | 
 | ||||||
| services: | services: | ||||||
| 
 | 
 | ||||||
|   # External dependencies |   # External dependencies | ||||||
|   redis: |   redis: | ||||||
|     image: redis:alpine |     image: redis:alpine | ||||||
|     restart: always |  | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/redis:/data" |       - "{{ root }}/redis:/data" | ||||||
| 
 | 
 | ||||||
|   # Core services |   # Core services | ||||||
|   front: |   front: | ||||||
|     image: mailu/nginx:{{ version }} |     image: mailu/nginx:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     env: |  | ||||||
|       - TLS_FLAVOR={{ tls_flavor or 'letsencrypt' }} |  | ||||||
|       - ADMIN={{ expose_admin or 'no' }} |  | ||||||
|     ports: |     ports: | ||||||
|     {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} |     {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} | ||||||
|     {% if bind4 %} |     {% if bind4 %} | ||||||
|       - "$PUBLIC_IPV4:{{ port }}:{{ port }}" |       - "{{ bind4 }}:{{ port }}:{{ port }}" | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     {% if bind6 %} |     {% if bind6 %} | ||||||
|       - "$PUBLIC_IPV6:{{ port }}:{{ port }}" |       - "{{ bind6 }}:{{ port }}:{{ port }}" | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     {% if flavor in ('cert', 'mail') %} |  | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/certs:/certs" |       - "{{ root }}/certs:/certs" | ||||||
|     {% endif %} |  | ||||||
| 
 | 
 | ||||||
|   admin: |   admin: | ||||||
|     image: mailu/admin:{{ version }} |     image: mailu/admin:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     {% if not expose_admin %} |     {% if not admin_enabled %} | ||||||
|     ports: |     ports: | ||||||
|       - 127.0.0.1:8080:80 |       - 127.0.0.1:8080:80 | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/data:/data" |       - "{{ root }}/data:/data" | ||||||
|       - "$ROOT/dkim:/dkim" |       - "{{ root }}/dkim:/dkim" | ||||||
|     depends_on: |     depends_on: | ||||||
|       - redis |       - redis | ||||||
| 
 | 
 | ||||||
|   imap: |   imap: | ||||||
|     image: mailu/dovecot:{{ version }} |     image: mailu/dovecot:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/data:/data" |       - "{{ root }}/mail:/mail" | ||||||
|       - "$ROOT/mail:/mail" |       - "{{ root }}/overrides:/overrides" | ||||||
|       - "$ROOT/overrides:/overrides" |  | ||||||
|     depends_on: |     depends_on: | ||||||
|       - front |       - front | ||||||
| 
 | 
 | ||||||
|   smtp: |   smtp: | ||||||
|     image: mailu/postfix:{{ version }} |     image: mailu/postfix:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/data:/data" |       - "{{ root }}/overrides:/overrides" | ||||||
|       - "$ROOT/overrides:/overrides" |  | ||||||
|     depends_on: |     depends_on: | ||||||
|       - front |       - front | ||||||
| 
 | 
 | ||||||
|   # Optional services |   # Optional services | ||||||
|   {% if enable_antispam %} |   {% if antispam_enabled %} | ||||||
|   antispam: |   antispam: | ||||||
|     image: mailu/rspamd:{{ version }} |     image: mailu/rspamd:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/filter:/var/lib/rspamd" |       - "{{ root }}/filter:/var/lib/rspamd" | ||||||
|       - "$ROOT/dkim:/dkim" |       - "{{ root }}/dkim:/dkim" | ||||||
|       - "$ROOT/overrides/rspamd:/etc/rspamd/override.d" |       - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d" | ||||||
|     depends_on: |     depends_on: | ||||||
|       - front |       - front | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|   {% if enable_antivirus %} |   {% if antivirus_enabled %} | ||||||
|   antivirus: |   antivirus: | ||||||
|     image: mailu/clamav:{{ version }} |     image: mailu/clamav:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/filter:/data" |       - "{{ root }}/filter:/data" | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|   {% if enable_webdav %} |   {% if webdav_enabled %} | ||||||
|   webdav: |   webdav: | ||||||
|     image: mailu/radivale:{{ version }} |     image: mailu/radicale:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/dav:/data" |       - "{{ root }}/dav:/data" | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|   {% if enable_fetchmail %} |   {% if fetchmail_enabled %} | ||||||
|   fetchmail: |   fetchmail: | ||||||
|     image: mailu/fetchmail:{{ version }} |     image: mailu/fetchmail:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |  | ||||||
|       - "$ROOT/data:/data" |  | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|   # Webmail |   # Webmail | ||||||
|   {% if enable_webmail %} |   {% if webmail_type != 'none' %} | ||||||
|   webmail: |   webmail: | ||||||
|     image: mailu/{{ webmail }}:{{ version }} |     image: mailu/{{ webmail_type }}:{{ version }} | ||||||
|     restart: always |  | ||||||
|     env_file: {{ env }} |     env_file: {{ env }} | ||||||
|     volumes: |     volumes: | ||||||
|       - "$ROOT/webmail:/data" |       - "{{ root }}/webmail:/data" | ||||||
|     depends_on: |     depends_on: | ||||||
|       - imap |       - imap | ||||||
|   {% endif %} |   {% 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