Merge branch 'master' into fix-sender-checks
This commit is contained in:
@@ -4,7 +4,7 @@ RUN mkdir -p /app
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY requirements-prod.txt requirements.txt
|
COPY requirements-prod.txt requirements.txt
|
||||||
RUN apk add --no-cache openssl \
|
RUN apk add --no-cache openssl curl \
|
||||||
&& apk add --no-cache --virtual build-dep openssl-dev libffi-dev python-dev build-base \
|
&& apk add --no-cache --virtual build-dep openssl-dev libffi-dev python-dev build-base \
|
||||||
&& pip install -r requirements.txt \
|
&& pip install -r requirements.txt \
|
||||||
&& apk del --no-cache build-dep
|
&& apk del --no-cache build-dep
|
||||||
@@ -20,3 +20,5 @@ EXPOSE 80/tcp
|
|||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD ["/start.sh"]
|
CMD ["/start.sh"]
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost/ui || exit 1
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ if exists "X-Virus" {
|
|||||||
stop;
|
stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if user.reply_enabled %}
|
{% if user.reply_active %}
|
||||||
if currentdate :value "le" "date" "{{ user.reply_enddate }}"
|
vacation :days 1 :subject "{{ user.reply_subject }}" "{{ user.reply_body }}";
|
||||||
{
|
|
||||||
vacation :days 1 :subject "{{ user.reply_subject }}" "{{ user.reply_body }}";
|
|
||||||
}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -272,6 +272,8 @@ class User(Base, Email):
|
|||||||
reply_enabled = db.Column(db.Boolean(), nullable=False, default=False)
|
reply_enabled = db.Column(db.Boolean(), nullable=False, default=False)
|
||||||
reply_subject = db.Column(db.String(255), nullable=True, default=None)
|
reply_subject = db.Column(db.String(255), nullable=True, default=None)
|
||||||
reply_body = db.Column(db.Text(), nullable=True, default=None)
|
reply_body = db.Column(db.Text(), nullable=True, default=None)
|
||||||
|
reply_startdate = db.Column(db.Date, nullable=False,
|
||||||
|
default=date(1900, 1, 1))
|
||||||
reply_enddate = db.Column(db.Date, nullable=False,
|
reply_enddate = db.Column(db.Date, nullable=False,
|
||||||
default=date(2999, 12, 31))
|
default=date(2999, 12, 31))
|
||||||
|
|
||||||
@@ -288,6 +290,25 @@ class User(Base, Email):
|
|||||||
def get_id(self):
|
def get_id(self):
|
||||||
return self.email
|
return self.email
|
||||||
|
|
||||||
|
@property
|
||||||
|
def destination(self):
|
||||||
|
if self.forward_enabled:
|
||||||
|
result = self.self.forward_destination
|
||||||
|
if self.forward_keep:
|
||||||
|
result += ',' + self.email
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return self.email
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reply_active(self):
|
||||||
|
now = date.today()
|
||||||
|
return (
|
||||||
|
self.reply_enabled and
|
||||||
|
self.reply_startdate < now and
|
||||||
|
self.reply_enddate > now
|
||||||
|
)
|
||||||
|
|
||||||
scheme_dict = {'PBKDF2': "pbkdf2_sha512",
|
scheme_dict = {'PBKDF2': "pbkdf2_sha512",
|
||||||
'BLF-CRYPT': "bcrypt",
|
'BLF-CRYPT': "bcrypt",
|
||||||
'SHA512-CRYPT': "sha512_crypt",
|
'SHA512-CRYPT': "sha512_crypt",
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ class UserReplyForm(flask_wtf.FlaskForm):
|
|||||||
reply_subject = fields.StringField(_('Reply subject'))
|
reply_subject = fields.StringField(_('Reply subject'))
|
||||||
reply_body = fields.StringField(_('Reply body'),
|
reply_body = fields.StringField(_('Reply body'),
|
||||||
widget=widgets.TextArea())
|
widget=widgets.TextArea())
|
||||||
|
reply_startdate = fields.html5.DateField(_('Start of vacation'))
|
||||||
reply_enddate = fields.html5.DateField(_('End of vacation'))
|
reply_enddate = fields.html5.DateField(_('End of vacation'))
|
||||||
submit = fields.SubmitField(_('Update'))
|
submit = fields.SubmitField(_('Update'))
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<form class="form" method="post" role="form">
|
<form class="form" method="post" role="form">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ macros.form_field(form.reply_enabled,
|
{{ macros.form_field(form.reply_enabled,
|
||||||
onchange="if(this.checked){$('#reply_subject,#reply_body,#reply_enddate').removeAttr('readonly')}
|
onchange="if(this.checked){$('#reply_subject,#reply_body,#reply_enddate,#reply_startdate').removeAttr('readonly')}
|
||||||
else{$('#reply_subject,#reply_body,#reply_enddate').attr('readonly', '')}") }}
|
else{$('#reply_subject,#reply_body,#reply_enddate').attr('readonly', '')}") }}
|
||||||
{{ macros.form_field(form.reply_subject,
|
{{ macros.form_field(form.reply_subject,
|
||||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||||
{{ macros.form_field(form.reply_enddate,
|
{{ macros.form_field(form.reply_enddate,
|
||||||
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||||
|
{{ macros.form_field(form.reply_startdate,
|
||||||
|
**{("rw" if user.reply_enabled else "readonly"): ""}) }}
|
||||||
|
|
||||||
{{ macros.form_field(form.submit) }}
|
{{ macros.form_field(form.submit) }}
|
||||||
</form>
|
</form>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|||||||
24
core/admin/migrations/versions/3b281286c7bd_.py
Normal file
24
core/admin/migrations/versions/3b281286c7bd_.py
Normal file
@@ -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')
|
||||||
@@ -13,3 +13,5 @@ EXPOSE 110/tcp 143/tcp 993/tcp 4190/tcp 2525/tcp
|
|||||||
VOLUME ["/data", "/mail"]
|
VOLUME ["/data", "/mail"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=350s CMD echo QUIT|nc localhost 110|grep "Dovecot ready."
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM alpine:3.8
|
FROM alpine:3.8
|
||||||
|
|
||||||
RUN apk add --no-cache certbot nginx nginx-mod-mail openssl \
|
RUN apk add --no-cache certbot nginx nginx-mod-mail openssl curl \
|
||||||
python py-jinja2 py-requests-toolbelt py-pip \
|
python py-jinja2 py-requests-toolbelt py-pip \
|
||||||
&& pip install --upgrade pip \
|
&& pip install --upgrade pip \
|
||||||
&& pip install idna
|
&& pip install idna
|
||||||
@@ -12,3 +12,5 @@ EXPOSE 80/tcp 443/tcp 110/tcp 143/tcp 465/tcp 587/tcp 993/tcp 995/tcp 25/tcp 100
|
|||||||
VOLUME ["/certs"]
|
VOLUME ["/certs"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -k -f -L http://localhost/health || exit 1
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ http {
|
|||||||
'' $scheme;
|
'' $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Disable the main http server when on kubernetes (port 80 and 443)
|
||||||
|
{% if KUBERNETES_INGRESS != 'true' %}
|
||||||
# Main HTTP server
|
# Main HTTP server
|
||||||
server {
|
server {
|
||||||
# Variables for proxifying
|
# Variables for proxifying
|
||||||
@@ -48,8 +50,8 @@ http {
|
|||||||
|
|
||||||
# Only enable HTTPS if TLS is enabled with no error
|
# Only enable HTTPS if TLS is enabled with no error
|
||||||
{% if TLS and not TLS_ERROR %}
|
{% if TLS and not TLS_ERROR %}
|
||||||
listen 443 ssl;
|
listen 443 ssl http2;
|
||||||
listen [::]:443 ssl;
|
listen [::]:443 ssl http2;
|
||||||
|
|
||||||
include /etc/nginx/tls.conf;
|
include /etc/nginx/tls.conf;
|
||||||
ssl_session_cache shared:SSLHTTP:50m;
|
ssl_session_cache shared:SSLHTTP:50m;
|
||||||
@@ -148,7 +150,12 @@ http {
|
|||||||
proxy_pass_request_body off;
|
proxy_pass_request_body off;
|
||||||
proxy_set_header Content-Length "";
|
proxy_set_header Content-Length "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /health {
|
||||||
|
return 204;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# Forwarding authentication server
|
# Forwarding authentication server
|
||||||
server {
|
server {
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ EXPOSE 25/tcp 10025/tcp
|
|||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=350s CMD echo QUIT|nc localhost 25|grep "220 .* ESMTP Postfix"
|
||||||
|
|||||||
@@ -2,13 +2,21 @@ FROM python:3-alpine
|
|||||||
|
|
||||||
COPY requirements.txt /requirements.txt
|
COPY requirements.txt /requirements.txt
|
||||||
|
|
||||||
|
ARG version=master
|
||||||
|
ENV VERSION=$version
|
||||||
|
|
||||||
RUN pip install -r /requirements.txt \
|
RUN pip install -r /requirements.txt \
|
||||||
&& apk add --no-cache nginx \
|
&& apk add --no-cache nginx curl \
|
||||||
&& mkdir /run/nginx
|
&& mkdir /run/nginx
|
||||||
|
|
||||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY . /docs
|
COPY . /docs
|
||||||
|
|
||||||
RUN sphinx-build /docs /build
|
RUN mkdir -p /build/$VERSION \
|
||||||
|
&& sphinx-build /docs /build/$VERSION
|
||||||
|
|
||||||
|
EXPOSE 80/tcp
|
||||||
|
|
||||||
CMD nginx -g "daemon off;"
|
CMD nginx -g "daemon off;"
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1
|
||||||
|
|||||||
9
docs/_templates/layout.html
vendored
9
docs/_templates/layout.html
vendored
@@ -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 %}
|
||||||
|
|||||||
4
docs/_templates/page.html
vendored
4
docs/_templates/page.html
vendored
@@ -1,4 +0,0 @@
|
|||||||
{%- extends "layout.html" %}
|
|
||||||
{% block body %}
|
|
||||||
{{ body|replace("VERSION_TAG", version) }}
|
|
||||||
{% endblock %}
|
|
||||||
16
docs/_templates/versions.html
vendored
Normal file
16
docs/_templates/versions.html
vendored
Normal file
@@ -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>
|
||||||
|
|
||||||
13
docs/conf.py
13
docs/conf.py
@@ -2,6 +2,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
extensions = ['sphinx.ext.imgmath', 'sphinx.ext.viewcode']
|
extensions = ['sphinx.ext.imgmath', 'sphinx.ext.viewcode']
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
source_suffix = '.rst'
|
source_suffix = '.rst'
|
||||||
@@ -9,9 +11,9 @@ master_doc = 'index'
|
|||||||
project = 'Mailu'
|
project = 'Mailu'
|
||||||
copyright = '2018, Mailu authors'
|
copyright = '2018, Mailu authors'
|
||||||
author = 'Mailu authors'
|
author = 'Mailu authors'
|
||||||
version = release = 'latest'
|
version = release = os.environ.get('VERSION', 'master')
|
||||||
language = None
|
language = None
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'Dockerfile', 'docker-compose.yml']
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
todo_include_todos = False
|
todo_include_todos = False
|
||||||
html_theme = 'sphinx_rtd_theme'
|
html_theme = 'sphinx_rtd_theme'
|
||||||
@@ -33,6 +35,11 @@ html_context = {
|
|||||||
'display_github': True,
|
'display_github': True,
|
||||||
'github_user': 'mailu',
|
'github_user': 'mailu',
|
||||||
'github_repo': 'mailu',
|
'github_repo': 'mailu',
|
||||||
'github_version': 'master',
|
'github_version': version,
|
||||||
|
'stable_version': '1.5',
|
||||||
|
'versions': [
|
||||||
|
('1.5', '/1.5/'),
|
||||||
|
('master', '/master/')
|
||||||
|
],
|
||||||
'conf_py_path': '/docs/'
|
'conf_py_path': '/docs/'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ Documentation is maintained in the ``docs`` directory and are maintained as `reS
|
|||||||
docker build -t docs docs
|
docker build -t docs docs
|
||||||
docker run -p 127.0.0.1:8080:80 docs
|
docker run -p 127.0.0.1:8080:80 docs
|
||||||
|
|
||||||
You can now read the local documentation by navigating to http://localhost:8080.
|
In a local build Docker always assumes the version to be master.
|
||||||
|
You can read the local documentation by navigating to http://localhost:8080/master.
|
||||||
|
|
||||||
.. note:: After modifying the documentation, the image needs to be rebuild and the container restarted for the changes to become visible.
|
.. note:: After modifying the documentation, the image needs to be rebuild and the container restarted for the changes to become visible.
|
||||||
|
|
||||||
|
|||||||
21
docs/docker-compose.yml
Normal file
21
docs/docker-compose.yml
Normal file
@@ -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/
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ the version of Mailu that you are running.
|
|||||||
configuration
|
configuration
|
||||||
compose/requirements
|
compose/requirements
|
||||||
compose/setup
|
compose/setup
|
||||||
kubernetes/stable/index
|
kubernetes/mailu/index
|
||||||
dns
|
dns
|
||||||
reverse
|
reverse
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
86
docs/kubernetes/mailu/admin-ingress.yaml
Normal file
86
docs/kubernetes/mailu/admin-ingress.yaml
Normal file
@@ -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:
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
VERSION: "master"
|
VERSION: "master"
|
||||||
|
|
||||||
# Set to a randomly generated 16 bytes string
|
# Set to a randomly generated 16 bytes string
|
||||||
SECRET_KEY: "YourKeyHere"
|
SECRET_KEY: "MySup3rS3cr3tPas"
|
||||||
|
|
||||||
# Address where listening ports should bind
|
# Address where listening ports should bind
|
||||||
BIND_ADDRESS4: "127.0.0.1"
|
BIND_ADDRESS4: "127.0.0.1"
|
||||||
@@ -45,6 +45,14 @@
|
|||||||
# Opt-out of statistics, replace with "True" to opt out
|
# Opt-out of statistics, replace with "True" to opt out
|
||||||
DISABLE_STATISTICS: "False"
|
DISABLE_STATISTICS: "False"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Kubernetes configuration
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Use Kubernetes Ingress Controller to handle all actions on port 80 and 443
|
||||||
|
# This way we can make use of the advantages of the cert-manager deployment
|
||||||
|
KUBERNETES_INGRESS: "true"
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# Optional features
|
# Optional features
|
||||||
###################################
|
###################################
|
||||||
@@ -71,19 +79,18 @@
|
|||||||
# Default: accept messages up to 50MB
|
# Default: accept messages up to 50MB
|
||||||
MESSAGE_SIZE_LIMIT: "50000000"
|
MESSAGE_SIZE_LIMIT: "50000000"
|
||||||
|
|
||||||
# Networks granted relay permissions, make sure that you include your Docker
|
|
||||||
# internal network (default to 172.17.0.0/16)
|
|
||||||
# For kubernetes this is the CIDR of the pod network
|
|
||||||
RELAYNETS: "10.2.0.0/16"
|
|
||||||
POD_ADDRESS_RANGE: "10.2.0.0/16"
|
|
||||||
|
|
||||||
|
|
||||||
# Will relay all outgoing mails if configured
|
# Will relay all outgoing mails if configured
|
||||||
#RELAYHOST=
|
#RELAYHOST=
|
||||||
|
|
||||||
# This part is needed for the XCLIENT login for postfix. This should be the POD ADDRESS range
|
# This part is needed for the XCLIENT login for postfix. This should be the POD ADDRESS range
|
||||||
FRONT_ADDRESS: "front.mailu-mailserver.svc.cluster.local"
|
FRONT_ADDRESS: "front.mailu-mailserver.svc.cluster.local"
|
||||||
|
|
||||||
|
# This value is needed by the webmail to find the correct imap backend
|
||||||
|
IMAP_ADDRESS: "imap.mailu-mailserver.svc.cluster.local"
|
||||||
|
|
||||||
|
# This value is used by Dovecot to find the Redis server in the cluster
|
||||||
|
REDIS_ADDRESS: "redis.mailu-mailserver.svc.cluster.local"
|
||||||
|
|
||||||
# Fetchmail delay
|
# Fetchmail delay
|
||||||
FETCHMAIL_DELAY: "600"
|
FETCHMAIL_DELAY: "600"
|
||||||
|
|
||||||
@@ -106,13 +113,16 @@
|
|||||||
###################################
|
###################################
|
||||||
|
|
||||||
# Path to the admin interface if enabled
|
# Path to the admin interface if enabled
|
||||||
|
# Kubernetes addition: You need to change ALL the ingresses, when you want this URL to be different!!!
|
||||||
WEB_ADMIN: "/admin"
|
WEB_ADMIN: "/admin"
|
||||||
|
|
||||||
# Path to the webmail if enabled
|
# Path to the webmail if enabled
|
||||||
|
# Currently, this is not used, because we intended to use a different subdomain: webmail.example.com
|
||||||
|
# This option can be added in a feature release
|
||||||
WEB_WEBMAIL: "/webmail"
|
WEB_WEBMAIL: "/webmail"
|
||||||
|
|
||||||
# Website name
|
# Website name
|
||||||
SITENAME: "AppSynth"
|
SITENAME: "Mailu"
|
||||||
|
|
||||||
# Linked Website URL
|
# Linked Website URL
|
||||||
WEBSITE: "https://example.com"
|
WEBSITE: "https://example.com"
|
||||||
@@ -1,23 +1,41 @@
|
|||||||
|
apiVersion: apps/v1beta2
|
||||||
apiVersion: extensions/v1beta1
|
kind: DaemonSet
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
metadata:
|
||||||
name: mailu-front
|
name: mailu-front
|
||||||
namespace: mailu-mailserver
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
k8s-app: mail-loadbalancer
|
||||||
|
component: ingress-controller
|
||||||
|
type: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: mail-loadbalancer
|
||||||
|
component: ingress-controller
|
||||||
|
type: nginx
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: mailu-front
|
k8s-app: mail-loadbalancer
|
||||||
role: mail
|
component: ingress-controller
|
||||||
tier: backend
|
type: nginx
|
||||||
spec:
|
spec:
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: node-role.kubernetes.io/node
|
||||||
|
operator: Exists
|
||||||
|
hostNetwork: true
|
||||||
|
nodeSelector:
|
||||||
|
node-role.kubernetes.io/node: ""
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
containers:
|
containers:
|
||||||
- name: front
|
- name: front
|
||||||
image: mailu/nginx:latest
|
image: mailu/nginx:master
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
@@ -26,12 +44,6 @@ spec:
|
|||||||
- name: certs
|
- name: certs
|
||||||
mountPath: /certs
|
mountPath: /certs
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
|
||||||
containerPort: 80
|
|
||||||
protocol: TCP
|
|
||||||
- name: https
|
|
||||||
containerPort: 443
|
|
||||||
protocol: TCP
|
|
||||||
- name: pop3
|
- name: pop3
|
||||||
containerPort: 110
|
containerPort: 110
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
@@ -85,21 +97,15 @@ metadata:
|
|||||||
name: front
|
name: front
|
||||||
namespace: mailu-mailserver
|
namespace: mailu-mailserver
|
||||||
labels:
|
labels:
|
||||||
app: mailu-admin
|
k8s-app: mail-loadbalancer
|
||||||
role: mail
|
component: ingress-controller
|
||||||
tier: backend
|
type: nginx
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: mailu-front
|
k8s-app: mail-loadbalancer
|
||||||
role: mail
|
component: ingress-controller
|
||||||
tier: backend
|
type: nginx
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
protocol: TCP
|
|
||||||
- name: https
|
|
||||||
port: 443
|
|
||||||
protocol: TCP
|
|
||||||
- name: pop3
|
- name: pop3
|
||||||
port: 110
|
port: 110
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
@@ -37,8 +37,8 @@ spec:
|
|||||||
- containerPort: 4190
|
- containerPort: 4190
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: 500Mi
|
memory: 1Gi
|
||||||
cpu: 500m
|
cpu: 1000m
|
||||||
limits:
|
limits:
|
||||||
memory: 1Gi
|
memory: 1Gi
|
||||||
cpu: 1000m
|
cpu: 1000m
|
||||||
193
docs/kubernetes/mailu/index.rst
Normal file
193
docs/kubernetes/mailu/index.rst
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
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. We have chosen to have a double NGINX stack for Mailu.
|
||||||
|
- ``Cert manager``: Creates automatic Lets Encrypt certificates based on an ``Ingress``-objects domain name.
|
||||||
|
- ``Mailu NGINX Front daemonset``: This daemonset runs in parallel with the Nginx Ingress Controller and only listens on all E-mail specific ports (25, 110, 143, 587,...)
|
||||||
|
- ``Mailu components``: All Mailu components (imap, smtp, security, webmail,...) are split into separate files to make them more handy to use, you can find the ``YAML`` files in this directory
|
||||||
|
|
||||||
|
What you need
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- A working Kubernetes cluster (tested with 1.10.5)
|
||||||
|
- A working `cert-manager`_ installation
|
||||||
|
- A working nginx-ingress controller needed for the lets-encrypt
|
||||||
|
certificates. You can find those files in the ``nginx`` subfolder
|
||||||
|
|
||||||
|
Cert manager
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The ``Cert-manager`` is quite easy to deploy using Helm when reading the
|
||||||
|
`docs`_. After booting the ``Cert-manager`` you’ll need a
|
||||||
|
``ClusterIssuer`` which takes care of all required certificates through
|
||||||
|
``Ingress`` items. We chose to provide a ``clusterIssuer`` so you can provide SSL certificates
|
||||||
|
for other namespaces (different websites/services), if you don't need this option, you can easily change this by
|
||||||
|
changing ``clusterIssuer`` to ``Issuer`` and adding the ``namespace: mailu-mailserver`` to the metadata.
|
||||||
|
An example of a production and a staging ``clusterIssuer``:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
# This clusterIssuer example uses the staging environment for testing first
|
||||||
|
apiVersion: certmanager.k8s.io/v1alpha1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: letsencrypt-stage
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
email: something@example.com
|
||||||
|
http01: {}
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: letsencrypt-stage
|
||||||
|
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
# This clusterIssuer example uses the production environment
|
||||||
|
apiVersion: certmanager.k8s.io/v1alpha1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
email: something@example.com
|
||||||
|
http01: {}
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
server: https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
|
||||||
|
**IMPORTANT**: All ``*-ingress.yaml`` files use the ``letsencrypt-stage`` ``clusterIssuer``. If you are ready for production,
|
||||||
|
change this field in all ``*-ingress.yaml`` files to ``letsencrypt-prod`` or whatever name you chose for the production.
|
||||||
|
If you choose for ``Issuer`` instead of ``clusterIssuer`` you also need to change the annotation to ``certmanager.k8s.io/issuer`` instead of ``certmanager.k8s.io/cluster-issuer``
|
||||||
|
|
||||||
|
Deploying Mailu
|
||||||
|
---------------
|
||||||
|
|
||||||
|
All manifests can be found in the ``mailu`` subdirectory. All commands
|
||||||
|
below need to be run from this subdirectory
|
||||||
|
|
||||||
|
Personalization
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- All services run in the same namespace, currently ``mailu-mailserver``. So if you want to use a different one, change the ``namespace`` value in **every** file
|
||||||
|
- Check the ``storage-class`` field in the ``pvc.yaml`` file, you can also change the sizes to your liking. Note that you need ``RWX`` (read-write-many) and ``RWO`` (read-write-once) storageclasses.
|
||||||
|
- Check the ``configmap.yaml`` and adapt it to your needs. Be sure to check the kubernetes DNS values at the end (if you use a different namespace)
|
||||||
|
- Check the ``*-ingress.yaml`` files and change it to the domain you want (this is for the kubernetes ingress controller to handle the admin, webmail, webdav and auth connections)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Boot the Mailu components
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To start Mailu, run the following commands from the ``docs/kubernetes/mailu`` directory
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
kubectl create -f rbac.yaml
|
||||||
|
kubectl create -f configmap.yaml
|
||||||
|
kubectl create -f pvc.yaml
|
||||||
|
kubectl create -f redis.yaml
|
||||||
|
kubectl create -f front.yaml
|
||||||
|
kubectl create -f webmail.yaml
|
||||||
|
kubectl create -f imap.yaml
|
||||||
|
kubectl create -f security.yaml
|
||||||
|
kubectl create -f smtp.yaml
|
||||||
|
kubectl create -f fetchmail.yaml
|
||||||
|
kubectl create -f admin.yaml
|
||||||
|
kubectl create -f webdav.yaml
|
||||||
|
kubectl create -f admin-ingress.yaml
|
||||||
|
kubectl create -f webdav-ingress.yaml
|
||||||
|
kubectl create -f security-ingress.yaml
|
||||||
|
kubectl create -f webmail-ingress.yaml
|
||||||
|
|
||||||
|
|
||||||
|
Create the first admin account
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When the cluster is online you need to create you master user to access https://mail.example.com/admin
|
||||||
|
Enter the main ``admin`` pod to create the root account:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
kubectl -n mailu-mailserver get po
|
||||||
|
kubectl -n mailu-mailserver exec -it mailu-admin-.... /bin/sh
|
||||||
|
|
||||||
|
And in the pod run the following command. The command uses following entries:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
python manage.py admin root example.com password
|
||||||
|
|
||||||
|
- ``admin`` Make it an admin user
|
||||||
|
- ``root`` The first part of the e-mail adres (ROOT@example.com)
|
||||||
|
- ``example.com`` the domain appendix
|
||||||
|
- ``password`` the chosen password for the user
|
||||||
|
|
||||||
|
|
||||||
|
Now you should be able to login on the mail account: https://mail.example.com/admin
|
||||||
|
|
||||||
|
Adaptations
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Dovecot
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- If you are using Dovecot on a shared file system (Glusterfs, NFS,...), you need to create a special override otherwise a lot of indexing errors will occur on your Dovecot pod.
|
||||||
|
- I also higher the number of max connections per IP. Now it's limited to 10.
|
||||||
|
|
||||||
|
Enter the dovecot pod:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
kubectl -n mailu-mailserver get po
|
||||||
|
kubectl -n mailu-mailserver exec -it mailu-imap-.... /bin/sh
|
||||||
|
|
||||||
|
Create the file ``overrides/dovecot.conf``
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
vi /overrides/dovecot.conf
|
||||||
|
|
||||||
|
And enter following contents:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
mail_nfs_index = yes
|
||||||
|
mail_nfs_storage = yes
|
||||||
|
mail_fsync = always
|
||||||
|
mmap_disable = yes
|
||||||
|
mail_max_userip_connections=100
|
||||||
|
|
||||||
|
Save and close the file and delete the imap pod to get it recreated.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
kubectl -n mailu-mailserver delete po/mailu-imap-....
|
||||||
|
|
||||||
|
Wait for the pod to recreate and you're online!
|
||||||
|
Happy mailing!
|
||||||
|
|
||||||
|
.. _here: https://github.com/hacor/Mailu/blob/master/core/postfix/conf/main.cf#L35
|
||||||
|
.. _cert-manager: https://github.com/jetstack/cert-manager
|
||||||
|
.. _docs: https://cert-manager.readthedocs.io/en/latest/getting-started/2-installing.html
|
||||||
|
|
||||||
|
Imap login fix
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If it seems you're not able to login using IMAP on your Mailu accounts, check the logs of the imap container to see whether it's a permissions problem on the database.
|
||||||
|
This problem can be easily fixed by running following commands:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
kubectl -n mailu-mailserver exec -it maolu-imap-... /bin/sh
|
||||||
|
chmod 777 /data/main.db
|
||||||
30
docs/kubernetes/mailu/security-ingress.yaml
Normal file
30
docs/kubernetes/mailu/security-ingress.yaml
Normal file
@@ -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
|
||||||
@@ -31,6 +31,9 @@ spec:
|
|||||||
- name: antispam
|
- name: antispam
|
||||||
containerPort: 11332
|
containerPort: 11332
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
- name: antispam-http
|
||||||
|
containerPort: 11334
|
||||||
|
protocol: TCP
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: filter
|
- name: filter
|
||||||
subPath: filter
|
subPath: filter
|
||||||
@@ -87,6 +90,9 @@ spec:
|
|||||||
- name: antispam
|
- name: antispam
|
||||||
port: 11332
|
port: 11332
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
- name: antispam-http
|
||||||
|
protocol: TCP
|
||||||
|
port: 11334
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,10 +21,10 @@ spec:
|
|||||||
name: mailu-config
|
name: mailu-config
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: 500Mi
|
memory: 2Gi
|
||||||
cpu: 200m
|
cpu: 500m
|
||||||
limits:
|
limits:
|
||||||
memory: 1Gi
|
memory: 2Gi
|
||||||
cpu: 500m
|
cpu: 500m
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /data
|
- mountPath: /data
|
||||||
46
docs/kubernetes/mailu/webdav-ingress.yaml
Normal file
46
docs/kubernetes/mailu/webdav-ingress.yaml
Normal file
@@ -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
|
||||||
31
docs/kubernetes/mailu/webmail-ingress.yaml
Normal file
31
docs/kubernetes/mailu/webmail-ingress.yaml
Normal file
@@ -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
|
||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: roundcube
|
- name: roundcube
|
||||||
image: mailu/roundcube:1.5
|
image: mailu/roundcube:master
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
@@ -2,15 +2,15 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
# keep it under 24 chars
|
# keep it under 24 chars
|
||||||
name: appsynth-lb
|
name: ingress-lb
|
||||||
namespace: kube-ingress
|
namespace: kube-ingress
|
||||||
labels:
|
labels:
|
||||||
k8s-app: appsynth-lb
|
k8s-app: ingress-lb
|
||||||
component: ingress-controller
|
component: ingress-controller
|
||||||
spec:
|
spec:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
selector:
|
selector:
|
||||||
k8s-app: appsynth-lb
|
k8s-app: ingress-lb
|
||||||
component: ingress-controller
|
component: ingress-controller
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
@@ -35,13 +35,6 @@ metadata:
|
|||||||
name: tcp-services
|
name: tcp-services
|
||||||
namespace: kube-ingress
|
namespace: kube-ingress
|
||||||
data:
|
data:
|
||||||
25: "mailu-mailserver/front:25"
|
|
||||||
110: "mailu-mailserver/front:110"
|
|
||||||
465: "mailu-mailserver/front:465"
|
|
||||||
587: "mailu-mailserver/front:587"
|
|
||||||
143: "mailu-mailserver/front:143"
|
|
||||||
993: "mailu-mailserver/front:993"
|
|
||||||
995: "mailu-mailserver/front:995"
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -61,7 +54,7 @@ metadata:
|
|||||||
prometheus.io/port: "10254"
|
prometheus.io/port: "10254"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
labels:
|
labels:
|
||||||
k8s-app: appsynth-lb
|
k8s-app: ingress-lb
|
||||||
component: ingress-controller
|
component: ingress-controller
|
||||||
type: nginx
|
type: nginx
|
||||||
spec:
|
spec:
|
||||||
@@ -71,13 +64,13 @@ spec:
|
|||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
k8s-app: appsynth-lb
|
k8s-app: ingress-lb
|
||||||
component: ingress-controller
|
component: ingress-controller
|
||||||
type: nginx
|
type: nginx
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
k8s-app: appsynth-lb
|
k8s-app: ingress-lb
|
||||||
component: ingress-controller
|
component: ingress-controller
|
||||||
type: nginx
|
type: nginx
|
||||||
spec:
|
spec:
|
||||||
@@ -94,14 +87,11 @@ spec:
|
|||||||
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.16.2
|
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.16.2
|
||||||
args:
|
args:
|
||||||
- /nginx-ingress-controller
|
- /nginx-ingress-controller
|
||||||
- --configmap=$(POD_NAMESPACE)/tectonic-custom-error
|
|
||||||
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
||||||
#- --default-ssl-certificate=tectonic-system/tectonic-ingress-tls-secret
|
|
||||||
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
|
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
|
||||||
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
|
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
|
||||||
- --annotations-prefix=ingress.kubernetes.io
|
- --annotations-prefix=ingress.kubernetes.io
|
||||||
- --enable-ssl-passthrough
|
- --enable-ssl-passthrough
|
||||||
- --ingress-class=tectonic
|
|
||||||
# use downward API
|
# use downward API
|
||||||
env:
|
env:
|
||||||
- name: POD_NAME
|
- name: POD_NAME
|
||||||
@@ -115,10 +105,8 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 80
|
containerPort: 80
|
||||||
hostPort: 80
|
|
||||||
- name: https
|
- name: https
|
||||||
containerPort: 443
|
containerPort: 443
|
||||||
hostPort: 443
|
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
@@ -134,6 +122,6 @@ spec:
|
|||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
node-role.kubernetes.io/node: ""
|
node-role.kubernetes.io/node: ""
|
||||||
dnsPolicy: ClusterFirst
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: 60
|
||||||
@@ -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
|
|
||||||
@@ -4,8 +4,11 @@ RUN apk add --no-cache clamav rsyslog wget clamav-libunrar
|
|||||||
|
|
||||||
COPY conf /etc/clamav
|
COPY conf /etc/clamav
|
||||||
COPY start.sh /start.sh
|
COPY start.sh /start.sh
|
||||||
|
COPY health.sh /health.sh
|
||||||
|
|
||||||
EXPOSE 3310/tcp
|
EXPOSE 3310/tcp
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD ["/start.sh"]
|
CMD ["/start.sh"]
|
||||||
|
|
||||||
|
HEALTHCHECK CMD /health.sh
|
||||||
|
|||||||
8
optional/clamav/health.sh
Executable file
8
optional/clamav/health.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$(echo PING | nc localhost 3310)" = "PONG" ]; then
|
||||||
|
echo "ping successful"
|
||||||
|
else
|
||||||
|
echo "ping failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM alpine:edge
|
FROM alpine:edge
|
||||||
|
|
||||||
RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
|
RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
|
||||||
&& apk add --no-cache radicale@testing py-dulwich@testing
|
&& apk add --no-cache radicale@testing py-dulwich@testing curl
|
||||||
|
|
||||||
COPY radicale.conf /radicale.conf
|
COPY radicale.conf /radicale.conf
|
||||||
|
|
||||||
@@ -9,3 +9,5 @@ EXPOSE 5232/tcp
|
|||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD radicale -f -S -C /radicale.conf
|
CMD radicale -f -S -C /radicale.conf
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost:5232/ || exit 1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM alpine:3.8
|
FROM alpine:3.8
|
||||||
|
|
||||||
RUN apk add --no-cache python py-jinja2 rspamd rspamd-controller rspamd-proxy rspamd-fuzzy ca-certificates py-pip \
|
RUN apk add --no-cache python py-jinja2 rspamd rspamd-controller rspamd-proxy rspamd-fuzzy ca-certificates py-pip curl\
|
||||||
&& pip install --upgrade pip \
|
&& pip install --upgrade pip \
|
||||||
&& pip install tenacity
|
&& pip install tenacity
|
||||||
|
|
||||||
@@ -14,3 +14,5 @@ EXPOSE 11332/tcp 11334/tcp 11335/tcp
|
|||||||
VOLUME ["/var/lib/rspamd"]
|
VOLUME ["/var/lib/rspamd"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=350s CMD curl -f -L http://localhost:11334/ || exit 1
|
||||||
|
|||||||
@@ -4,15 +4,19 @@ RUN mkdir -p /app
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY requirements.txt requirements.txt
|
COPY requirements.txt requirements.txt
|
||||||
RUN apk add --no-cache git \
|
RUN apk add --no-cache git curl \
|
||||||
&& pip install -r requirements.txt
|
&& pip install -r requirements.txt
|
||||||
|
|
||||||
COPY server.py ./server.py
|
COPY server.py ./server.py
|
||||||
COPY setup.py ./setup.py
|
COPY setup.py ./setup.py
|
||||||
COPY main.py ./main.py
|
COPY main.py ./main.py
|
||||||
|
COPY flavors /data/master/flavors
|
||||||
|
COPY templates /data/master/templates
|
||||||
|
|
||||||
RUN python setup.py https://github.com/mailu/mailu /data
|
#RUN python setup.py https://github.com/mailu/mailu /data
|
||||||
|
|
||||||
EXPOSE 80/tcp
|
EXPOSE 80/tcp
|
||||||
|
|
||||||
CMD gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload main:app
|
CMD gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload main:app
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ services:
|
|||||||
setup:
|
setup:
|
||||||
image: mailu/setup
|
image: mailu/setup
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "8000:80"
|
||||||
|
build: .
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Mailu main configuration file
|
# Mailu main configuration file
|
||||||
#
|
#
|
||||||
|
# Generated for {{ flavor }} flavor
|
||||||
|
#
|
||||||
# This file is autogenerated by the configuration management wizard.
|
# This file is autogenerated by the configuration management wizard.
|
||||||
# For a detailed list of configuration variables, see the documentation at
|
# For a detailed list of configuration variables, see the documentation at
|
||||||
# https://mailu.io
|
# https://mailu.io
|
||||||
@@ -9,60 +11,118 @@
|
|||||||
###################################
|
###################################
|
||||||
|
|
||||||
# Set this to the path where Mailu data and configuration is stored
|
# Set this to the path where Mailu data and configuration is stored
|
||||||
ROOT=/mailu
|
# This variable is now set directly in `docker-compose.yml by the setup utility
|
||||||
|
# ROOT={{ root }}
|
||||||
|
|
||||||
|
# Mailu version to run (1.0, 1.1, etc. or master)
|
||||||
|
#VERSION={{ version }}
|
||||||
|
|
||||||
# Set to a randomly generated 16 bytes string
|
# Set to a randomly generated 16 bytes string
|
||||||
SECRET_KEY={{ secret(16) }}
|
SECRET_KEY={{ secret(16) }}
|
||||||
|
|
||||||
# Address where listening ports should bind
|
# Address where listening ports should bind
|
||||||
{% if bind4 %}PUBLIC_IPV4={{ bind4 }}{% endif %}
|
# This variables are now set directly in `docker-compose.yml by the setup utility
|
||||||
{% if bind6 %}PUBLIC_IPV6={{ bind6 }}{% endif %}
|
# PUBLIC_IPV4= {{ bind4 }} (default: 127.0.0.1)
|
||||||
|
# PUBLIC_IPV6= {{ bind6 }} (default: ::1)
|
||||||
|
|
||||||
# Mail address of the postmaster
|
# Main mail domain
|
||||||
POSTMASTER={{ postmaster }}
|
DOMAIN={{ domain }}
|
||||||
|
|
||||||
# Hostnames for this server, separated with comas
|
# Hostnames for this server, separated with comas
|
||||||
HOSTNAMES={{ hostnames }}
|
HOSTNAMES={{ hostnames }}
|
||||||
|
|
||||||
|
# Postmaster local part (will append the main mail domain)
|
||||||
|
POSTMASTER={{ postmaster }}
|
||||||
|
|
||||||
|
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
|
||||||
|
TLS_FLAVOR={{ tls_flavor }}
|
||||||
|
|
||||||
# Authentication rate limit (per source IP address)
|
# Authentication rate limit (per source IP address)
|
||||||
AUTH_RATELIMIT={{ auth_ratelimit }}
|
{% if auth_ratelimit_pm > '0' and auth_ratelimit_ph > '0' %}
|
||||||
|
AUTH_RATELIMIT={{ auth_ratelimit_pm }}/minute;{{ auth_ratelimit_ph }}/hour
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# Opt-out of statistics, replace with "True" to opt out
|
# Opt-out of statistics, replace with "True" to opt out
|
||||||
DISABLE_STATISTICS={{ disable_statistics }}
|
DISABLE_STATISTICS={{ disable_statistics or 'False' }}
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# Server behavior
|
# Optional features
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Expose the admin interface (value: true, false)
|
||||||
|
ADMIN={{ admin_enabled or 'false' }}
|
||||||
|
|
||||||
|
# Choose which webmail to run if any (values: roundcube, rainloop, none)
|
||||||
|
WEBMAIL={{ webmail_type }}
|
||||||
|
|
||||||
|
# Dav server implementation (value: radicale, none)
|
||||||
|
WEBDAV={{ webdav_enabled or 'none' }}
|
||||||
|
|
||||||
|
# Antivirus solution (value: clamav, none)
|
||||||
|
#ANTIVIRUS={{ antivirus_enabled or 'none' }}
|
||||||
|
|
||||||
|
#Antispam solution
|
||||||
|
ANTISPAM={{ antispam_enabled or 'none'}}
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Mail settings
|
||||||
###################################
|
###################################
|
||||||
|
|
||||||
# Message size limit in bytes
|
# Message size limit in bytes
|
||||||
# Default: accept messages up to 50MB
|
# Default: accept messages up to 50MB
|
||||||
MESSAGE_SIZE_LIMIT={{ message_size_limit }}
|
MESSAGE_SIZE_LIMIT={{ message_size_limit or '50000000' }}
|
||||||
|
|
||||||
# Networks granted relay permissions, make sure that you include your Docker
|
# Networks granted relay permissions, make sure that you include your Docker
|
||||||
# internal network (default to 172.17.0.0/16)
|
# internal network (default to 172.17.0.0/16)
|
||||||
RELAYNETS={{ relaynets }}
|
RELAYNETS={{ relaynets or '172.17.0.0/16' }}
|
||||||
|
|
||||||
# Will relay all outgoing mails if configured
|
# Will relay all outgoing mails if configured
|
||||||
RELAYHOST={{ relayhost }}
|
RELAYHOST={{ relayhost }}
|
||||||
|
|
||||||
# Fetchmail delay
|
# Fetchmail delay
|
||||||
FETCHMAIL_DELAY={{ fetchmail_delay }}
|
FETCHMAIL_DELAY={{ fetchmail_delay or '600' }}
|
||||||
|
|
||||||
# Recipient delimiter, character used to delimiter localpart from custom address part
|
# Recipient delimiter, character used to delimiter localpart from custom address part
|
||||||
RECIPIENT_DELIMITER={{ recipient_delimiter }}
|
RECIPIENT_DELIMITER={{ recipient_delimiter or '+' }}
|
||||||
|
|
||||||
{% if dmarc_rua or dmarc_ruf %}
|
|
||||||
# DMARC rua and ruf email
|
# DMARC rua and ruf email
|
||||||
{% if dmarc_rua %}DMARC_RUA={{ dmarc_rua }}{% endif %}
|
DMARC_RUA={{ dmarc_rua or 'admin' }}
|
||||||
{% if dmarc_ruf %}DMARC_RUF={{ dmarc_ruf }}{% endif %}
|
DMARC_RUF={{ dmarc_ruf or 'admin' }}
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if welcome_enabled %}
|
{% if welcome_enabled %}
|
||||||
# Welcome email, enable and set a topic and body if you wish to send welcome
|
# Welcome email, enable and set a topic and body if you wish to send welcome
|
||||||
# emails to all users.
|
# emails to all users.
|
||||||
WELCOME={{ welcome_enable }}
|
WELCOME={{ welcome_enable or 'false' }}
|
||||||
WELCOME_SUBJECT={{ welcome_subject }}
|
WELCOME_SUBJECT={{ welcome_subject or 'Welcome to your new email account' }}
|
||||||
WELCOME_BODY={{ welcome_body }}
|
WELCOME_BODY={{ welcome_body or 'Welcome to your new email account, if you can read this, then it is configured properly!' }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Maildir Compression
|
||||||
|
# choose compression-method, default: none (value: bz2, gz)
|
||||||
|
COMPRESSION={{ compression }}
|
||||||
|
# change compression-level, default: 6 (value: 1-9)
|
||||||
|
COMPRESSION_LEVEL={{ compression_level }}
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Web settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Path to the admin interface if enabled
|
||||||
|
WEB_ADMIN={{ admin_path }}
|
||||||
|
|
||||||
|
# Path to the webmail if enabled
|
||||||
|
WEB_WEBMAIL={{ webmail_path }}
|
||||||
|
|
||||||
|
# Website name
|
||||||
|
SITENAME={{ site_name }}
|
||||||
|
|
||||||
|
# Linked Website URL
|
||||||
|
WEBSITE={{ website }}
|
||||||
|
|
||||||
|
{% if recaptcha_public_key and recaptcha_private_key %}
|
||||||
|
# Registration reCaptcha settings (warning, this has some privacy impact)
|
||||||
|
# RECAPTCHA_PUBLIC_KEY={{ recaptcha_public_key }}
|
||||||
|
# RECAPTCHA_PRIVATE_KEY={{ recaptcha_private_key }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if domain_registration %}
|
{% if domain_registration %}
|
||||||
@@ -70,39 +130,28 @@ WELCOME_BODY={{ welcome_body }}
|
|||||||
DOMAIN_REGISTRATION=true
|
DOMAIN_REGISTRATION=true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
###################################
|
|
||||||
# Web settings
|
|
||||||
###################################
|
|
||||||
|
|
||||||
# Path to the admin interface if enabled
|
|
||||||
WEB_ADMIN=/admin
|
|
||||||
|
|
||||||
# Path to the webmail if enabled
|
|
||||||
WEB_WEBMAIL=/webmail
|
|
||||||
|
|
||||||
# Website name
|
|
||||||
SITENAME=Mailu
|
|
||||||
|
|
||||||
# Linked Website URL
|
|
||||||
WEBSITE=https://mailu.io
|
|
||||||
|
|
||||||
{% if recaptcha_public_key and recaptcha_private_key %}
|
|
||||||
# Registration reCaptcha settings (warning, this has some privacy impact)
|
|
||||||
# RECAPTCHA_PUBLIC_KEY={{ recaptcha_public_key }}
|
|
||||||
# RECAPTCHA_PRIVATE_KEY={{ recaptcha_private_key }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# Advanced settings
|
# Advanced settings
|
||||||
###################################
|
###################################
|
||||||
|
|
||||||
{% if password_scheme %}
|
# Log driver for front service. Possible values:
|
||||||
# Specific password storage scheme
|
# json-file (default)
|
||||||
PASSWORD_SCHEME={{ password_scheme }}
|
# journald (On systemd platforms, useful for Fail2Ban integration)
|
||||||
{% endif %}
|
# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker-compose log` for front!)
|
||||||
|
LOG_DRIVER={{ log_driver or 'json-file' }}
|
||||||
|
|
||||||
|
# Docker-compose project name, this will prepended to containers names.
|
||||||
|
COMPOSE_PROJECT_NAME={{ compose_project_name or 'mailu' }}
|
||||||
|
|
||||||
|
# Default password scheme used for newly created accounts and changed passwords
|
||||||
|
# (value: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
|
||||||
|
PASSWORD_SCHEME={{ password_scheme or 'BLF-CRYPT' }}
|
||||||
|
|
||||||
# Header to take the real ip from
|
# Header to take the real ip from
|
||||||
REAL_IP_HEADER={{ real_ip_header }}
|
REAL_IP_HEADER={{ real_ip_header }}
|
||||||
|
|
||||||
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
|
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
|
||||||
REAL_IP_FROM={{ real_ip_from }}
|
REAL_IP_FROM={{ real_ip_from }}
|
||||||
|
|
||||||
|
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
|
||||||
|
REJECT_UNLISTED_RECIPIENT={{ reject_unlisted_recipient }}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
<p>Docker Compose expects a project file, named <code>docker-compose.yml</code>
|
<p>Docker Compose expects a project file, named <code>docker-compose.yml</code>
|
||||||
in a project directory. First create your project directory.</p>
|
in a project directory. First create your project directory.</p>
|
||||||
|
|
||||||
<pre><code>mkdir /mailu
|
<pre><code>mkdir {{ root }}
|
||||||
</pre></code>
|
</pre></code>
|
||||||
|
|
||||||
<p>Then download the project file. A side configuration file makes it easier
|
<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>
|
to read and check the configuration variables generated by the wizard.</p>
|
||||||
|
|
||||||
<pre><code>cd /mailu
|
<pre><code>cd {{ root }}
|
||||||
wget {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }}
|
curl {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }} > docker-compose.yml
|
||||||
wget {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }}
|
curl {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }} > mailu.env
|
||||||
</pre></code>
|
</pre></code>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
@@ -30,7 +30,22 @@ files before going any further.</p>
|
|||||||
<p>To start your compose project, simply run the Docker Compose <code>up</code>
|
<p>To start your compose project, simply run the Docker Compose <code>up</code>
|
||||||
command.</p>
|
command.</p>
|
||||||
|
|
||||||
<pre><code>cd /mailu
|
<pre><code>cd {{ root }}
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
</pre></code>
|
</pre></code>
|
||||||
|
|
||||||
|
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-compose exec admin 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 %}
|
{% endcall %}
|
||||||
|
|||||||
128
setup/flavors/stack/docker-compose.yml
Normal file
128
setup/flavors/stack/docker-compose.yml
Normal file
@@ -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 }}
|
||||||
1
setup/flavors/stack/mailu.env
Symbolic link
1
setup/flavors/stack/mailu.env
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../compose/mailu.env
|
||||||
60
setup/flavors/stack/setup.html
Normal file
60
setup/flavors/stack/setup.html
Normal file
@@ -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 %}
|
||||||
@@ -32,9 +32,11 @@ def secret(length=16):
|
|||||||
|
|
||||||
def build_app(path):
|
def build_app(path):
|
||||||
|
|
||||||
|
#Hardcoded master as the only version for test purposes
|
||||||
versions = [
|
versions = [
|
||||||
version for version in os.listdir(path)
|
# version for version in os.listdir(path)
|
||||||
if os.path.isdir(os.path.join(path, version))
|
# if os.path.isdir(os.path.join(path, version))
|
||||||
|
"master"
|
||||||
]
|
]
|
||||||
|
|
||||||
app.jinja_env.trim_blocks = True
|
app.jinja_env.trim_blocks = True
|
||||||
@@ -63,6 +65,12 @@ def build_app(path):
|
|||||||
def wizard():
|
def wizard():
|
||||||
return flask.render_template('wizard.html')
|
return flask.render_template('wizard.html')
|
||||||
|
|
||||||
|
@bp.route("/submit_flavor", methods=["POST"])
|
||||||
|
def submit_flavor():
|
||||||
|
data = flask.request.form.copy()
|
||||||
|
steps = sorted(os.listdir(path + "/" + version + "/templates/steps/" + data["flavor"]))
|
||||||
|
return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps)
|
||||||
|
|
||||||
@bp.route("/submit", methods=["POST"])
|
@bp.route("/submit", methods=["POST"])
|
||||||
def submit():
|
def submit():
|
||||||
data = flask.request.form.copy()
|
data = flask.request.form.copy()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<h1>Mailu configuration</h1>
|
<h1>Mailu configuration</h1>
|
||||||
<p>
|
<p>
|
||||||
Version
|
Version
|
||||||
<select onchange="window.location.href=this.value;">
|
<select onchange="window.location.href=this.value;" class="btn btn-primary dropdown-toggle">
|
||||||
{% for available in versions %}
|
{% for available in versions %}
|
||||||
<option value="{{ url_for('{}.wizard'.format(available)) }}" {% if available == version %}selected{% endif %}>{{ available }}</option>
|
<option value="{{ url_for('{}.wizard'.format(available)) }}" {% if available == version %}selected{% endif %}>{{ available }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro radio(name, value, emph, text) %}
|
{% macro radio(name, value, emph, text, current) %}
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="{{ name }}" value="{{ value }}">
|
<input type="radio" name="{{ name }}" value="{{ value }}"{% if current == value %} checked="checked"{% endif %}>
|
||||||
{% if emph %}
|
{% if emph %}
|
||||||
<strong>{{ emph }}</strong>,
|
<strong>{{ emph }}</strong>,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -3,31 +3,27 @@
|
|||||||
interface, Web email clients (webmails), antispam, antivirus, etc. If you
|
interface, Web email clients (webmails), antispam, antivirus, etc. If you
|
||||||
wish to disable some of these features, you are free to do so.</p>
|
wish to disable some of these features, you are free to do so.</p>
|
||||||
|
|
||||||
<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" checked></div>
|
|
||||||
<input class="form-control" type="text" name="admin_path" value="/admin">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>Emails will be available through IMAP and POP3. You may also enable a Web
|
<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
|
email client. These do add some complexity but provide an easier way of
|
||||||
accessing messages for beginner users.</p>
|
accessing messages for beginner users.</p>
|
||||||
|
|
||||||
|
<!-- Switched from radio buttons to dropdown menu in order to remove the checkbox -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Enable Web email client (and path to the Web email client)</label>
|
<label>Enable Web email client (and path to the Web email client)</label>
|
||||||
<div class="input-group">
|
<!-- <div class="radio"> -->
|
||||||
<div class="input-group-addon"><input type="checkbox" name="webmail_enabled" checked></div>
|
<!-- {{ macros.radio("webmail_type", "roundcube", "RoundCube", "popular Webmail running on top of PHP") }} -->
|
||||||
<input class="form-control" type="text" name="webmail_path" value="/webmail">
|
<!-- {{ macros.radio("webmail_type", "rainloop", "Rainloop", "lightweight Webmail based on PHP, no database") }} -->
|
||||||
</div>
|
<!-- </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>
|
<p></p>
|
||||||
<div class="radio">
|
<div class="input-group">
|
||||||
{{ macros.radio("webmail_type", "roundcube", "RoundCube", "popular Webmail running on top of PHP") }}
|
<!-- <div class="input-group-addon"><input type="checkbox" name="webmail_enabled" value="true"></div> -->
|
||||||
{{ macros.radio("webmail_type", "rainloop", "Rainloop", "lightweight Webmail based on PHP, no database") }}
|
<input class="form-control" type="text" name="webmail_path" value="/webmail">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -38,15 +34,29 @@ also disable the antivirus if required (it does use aroung 1GB of ram).</p>
|
|||||||
|
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input class="form-check-input" type="checkbox" name="antispam_enabled" checked>
|
<input class="form-check-input" type="checkbox" name="antispam_enabled" value="rspamd" checked>
|
||||||
Enable the filtering service
|
Enable the spam filtering service
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input class="form-check-input" type="checkbox" name="antivirus_enabled" checked>
|
<input class="form-check-input" type="checkbox" name="antivirus_enabled" value="clamav">
|
||||||
Enable the antivirus service
|
Enable the antivirus service
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</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 %}
|
{% endcall %}
|
||||||
39
setup/templates/steps/compose/03_expose.html
Normal file
39
setup/templates/steps/compose/03_expose.html
Normal file
@@ -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 %}
|
||||||
78
setup/templates/steps/config.html
Normal file
78
setup/templates/steps/config.html
Normal file
@@ -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 %}
|
|
||||||
@@ -7,10 +7,10 @@ developpers, will mostly cover Compose and Stack, while other flavors are
|
|||||||
maintained by specific contributors.</p>
|
maintained by specific contributors.</p>
|
||||||
|
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
{{ macros.radio("flavor", "compose", "Compose", "simply using Docker Compose manager") }}
|
{{ macros.radio("flavor", "compose", "Compose", "simply using Docker Compose manager", flavor) }}
|
||||||
{{ macros.radio("flavor", "stack", "Stack", "using stack deployments in a Swarm cluster") }}
|
{{ macros.radio("flavor", "stack", "Stack", "using stack deployments in a Swarm cluster", flavor) }}
|
||||||
{{ macros.radio("flavor", "rancher", "Rancher", "on top of the Rancher container manager") }}
|
{{ macros.radio("flavor", "rancher", "Rancher", "on top of the Rancher container manager", flavor) }}
|
||||||
{{ macros.radio("flavor", "kubernetes", "Kubernetes", "on top of the Kubernetes container manager") }}
|
{{ macros.radio("flavor", "kubernetes", "Kubernetes", "on top of the Kubernetes container manager", flavor) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endcall %}
|
{% 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 %}
|
|
||||||
62
setup/templates/steps/stack/02_services.html
Normal file
62
setup/templates/steps/stack/02_services.html
Normal file
@@ -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 %}
|
||||||
21
setup/templates/steps/stack/03_expose.html
Normal file
21
setup/templates/steps/stack/03_expose.html
Normal file
@@ -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 %}
|
||||||
@@ -8,12 +8,18 @@
|
|||||||
ready when using this wizard.
|
ready when using this wizard.
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
<form method="post" action="{{ url_for(".submit") }}">
|
<form method="post" action="{{ url_for(".submit_flavor") }}">
|
||||||
{% include "steps/flavor.html" %}
|
{% include "steps/flavor.html" %}
|
||||||
{% include "steps/expose.html" %}
|
<input class="btn btn-primary" type="submit" value="Next >" style="margin-bottom: 15px">
|
||||||
{% include "steps/services.html" %}
|
</form>
|
||||||
{% include "steps/optional.html" %}
|
{% if flavor %}
|
||||||
|
<form method="post" action="{{ url_for(".submit") }}">
|
||||||
|
<input type="hidden" name="flavor" value="{{ flavor }}">
|
||||||
|
{% include "steps/config.html" %}
|
||||||
|
{%for file in steps %}
|
||||||
|
{% include "steps/" + flavor + "/" + file %}
|
||||||
|
{% endfor %}
|
||||||
<input class="btn btn-primary" type="submit" value="Setup Mailu">
|
<input class="btn btn-primary" type="submit" value="Setup Mailu">
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ FROM php:7.2-apache
|
|||||||
ENV RAINLOOP_URL https://github.com/RainLoop/rainloop-webmail/releases/download/v1.12.1/rainloop-community-1.12.1.zip
|
ENV RAINLOOP_URL https://github.com/RainLoop/rainloop-webmail/releases/download/v1.12.1/rainloop-community-1.12.1.zip
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
unzip python3 python3-jinja2 \
|
unzip python3 python3-jinja2 curl \
|
||||||
&& rm -rf /var/www/html/ \
|
&& rm -rf /var/www/html/ \
|
||||||
&& mkdir /var/www/html \
|
&& mkdir /var/www/html \
|
||||||
&& cd /var/www/html \
|
&& cd /var/www/html \
|
||||||
@@ -29,3 +29,5 @@ EXPOSE 80/tcp
|
|||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ FROM php:7.2-apache
|
|||||||
ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.3.7/roundcubemail-1.3.7-complete.tar.gz
|
ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.3.7/roundcubemail-1.3.7-complete.tar.gz
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
zlib1g-dev \
|
zlib1g-dev curl \
|
||||||
&& docker-php-ext-install zip \
|
&& docker-php-ext-install zip \
|
||||||
&& echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \
|
&& echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \
|
||||||
&& rm -rf /var/www/html/ \
|
&& rm -rf /var/www/html/ \
|
||||||
@@ -28,3 +28,5 @@ EXPOSE 80/tcp
|
|||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD ["/start.sh"]
|
CMD ["/start.sh"]
|
||||||
|
|
||||||
|
HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user