Merged conflicts
commit
3098343360
@ -0,0 +1,10 @@
|
|||||||
|
rules:
|
||||||
|
default: null
|
||||||
|
branches:
|
||||||
|
master:
|
||||||
|
protection:
|
||||||
|
required_status_checks:
|
||||||
|
contexts:
|
||||||
|
- continuous-integration/travis-ci
|
||||||
|
required_pull_request_reviews:
|
||||||
|
required_approving_review_count: 2
|
@ -1,8 +1,22 @@
|
|||||||
language: python
|
sudo: required
|
||||||
python:
|
services: docker
|
||||||
- "3.6"
|
addons:
|
||||||
install:
|
apt:
|
||||||
- pip install -r docs/requirements.txt
|
packages:
|
||||||
|
- docker-ce
|
||||||
|
env:
|
||||||
|
- VERSION=$TRAVIS_BRANCH
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- sphinx-versioning build -b -B 1.5 -r 1.5 -w '^[0-9.]*$' -w master -W '^$' docs/ build/
|
# Default to mailu for DOCKER_ORG
|
||||||
- python "docs/conf.py" "build" "$DEPLOY_HOST" "$DEPLOY_USERNAME" "$DEPLOY_PASSWORD" "$DEPLOY_REMOTEDIR"
|
- if [ -z "$DOCKER_ORG" ]; then export DOCKER_ORG="mailu"; fi
|
||||||
|
- docker-compose -f tests/build.yml build
|
||||||
|
- tests/compose/test-script.sh
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
provider: script
|
||||||
|
script: bash tests/deploy.sh
|
||||||
|
on:
|
||||||
|
all_branches: true
|
||||||
|
condition: -n $DOCKER_UN
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
__all__ = [
|
||||||
|
'auth', 'postfix', 'dovecot', 'fetch'
|
||||||
|
]
|
@ -0,0 +1,40 @@
|
|||||||
|
from mailu import db, models
|
||||||
|
from mailu.internal import internal
|
||||||
|
|
||||||
|
import flask
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/dovecot/passdb/<user_email>")
|
||||||
|
def dovecot_passdb_dict(user_email):
|
||||||
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
|
return flask.jsonify({
|
||||||
|
"password": user.password,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/dovecot/userdb/<user_email>")
|
||||||
|
def dovecot_userdb_dict(user_email):
|
||||||
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
|
return flask.jsonify({
|
||||||
|
"quota_rule": "*:bytes={}".format(user.quota_bytes)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/dovecot/quota/<ns>/<user_email>", methods=["POST"])
|
||||||
|
def dovecot_quota(ns, user_email):
|
||||||
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
|
if ns == "storage":
|
||||||
|
user.quota_bytes_used = flask.request.get_json()
|
||||||
|
db.session.commit()
|
||||||
|
return flask.jsonify(None)
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/dovecot/sieve/name/<script>/<user_email>")
|
||||||
|
def dovecot_sieve_name(script, user_email):
|
||||||
|
return flask.jsonify(script)
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/dovecot/sieve/data/default/<user_email>")
|
||||||
|
def dovecot_sieve_data(user_email):
|
||||||
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
|
return flask.jsonify(flask.render_template("default.sieve", user=user))
|
@ -0,0 +1,32 @@
|
|||||||
|
from mailu import db, models
|
||||||
|
from mailu.internal import internal
|
||||||
|
|
||||||
|
import flask
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/fetch")
|
||||||
|
def fetch_list():
|
||||||
|
return flask.jsonify([
|
||||||
|
{
|
||||||
|
"id": fetch.id,
|
||||||
|
"tls": fetch.tls,
|
||||||
|
"keep": fetch.keep,
|
||||||
|
"user_email": fetch.user_email,
|
||||||
|
"protocol": fetch.protocol,
|
||||||
|
"host": fetch.host,
|
||||||
|
"port": fetch.port,
|
||||||
|
"username": fetch.username,
|
||||||
|
"password": fetch.password
|
||||||
|
} for fetch in models.Fetch.query.all()
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/fetch/<fetch_id>", methods=["POST"])
|
||||||
|
def fetch_done(fetch_id):
|
||||||
|
fetch = models.Fetch.query.get(fetch_id) or flask.abort(404)
|
||||||
|
fetch.last_check = datetime.datetime.now()
|
||||||
|
fetch.error_message = str(flask.request.get_json())
|
||||||
|
db.session.add(fetch)
|
||||||
|
db.session.commit()
|
||||||
|
return ""
|
@ -0,0 +1,54 @@
|
|||||||
|
from mailu import db, models
|
||||||
|
from mailu.internal import internal
|
||||||
|
|
||||||
|
import flask
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/postfix/domain/<domain_name>")
|
||||||
|
def postfix_mailbox_domain(domain_name):
|
||||||
|
domain = models.Domain.query.get(domain_name) or flask.abort(404)
|
||||||
|
return flask.jsonify(domain.name)
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/postfix/mailbox/<email>")
|
||||||
|
def postfix_mailbox_map(email):
|
||||||
|
user = models.User.query.get(email) or flask.abort(404)
|
||||||
|
return flask.jsonify(user.email)
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/postfix/alias/<alias>")
|
||||||
|
def postfix_alias_map(alias):
|
||||||
|
localpart, domain = alias.split('@', 1) if '@' in alias else (None, alias)
|
||||||
|
alternative = models.Alternative.query.get(domain)
|
||||||
|
if alternative:
|
||||||
|
domain = alternative.domain_name
|
||||||
|
email = '{}@{}'.format(localpart, domain)
|
||||||
|
if localpart is None:
|
||||||
|
return flask.jsonify(domain)
|
||||||
|
else:
|
||||||
|
alias_obj = models.Alias.resolve(localpart, domain)
|
||||||
|
if alias_obj:
|
||||||
|
return flask.jsonify(",".join(alias_obj.destination))
|
||||||
|
user_obj = models.User.query.get(email)
|
||||||
|
if user_obj:
|
||||||
|
return flask.jsonify(user_obj.destination)
|
||||||
|
return flask.abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/postfix/transport/<email>")
|
||||||
|
def postfix_transport(email):
|
||||||
|
localpart, domain = email.split('@', 1) if '@' in email else (None, email)
|
||||||
|
relay = models.Relay.query.get(domain) or flask.abort(404)
|
||||||
|
return flask.jsonify("smtp:[{}]".format(relay.smtp))
|
||||||
|
|
||||||
|
|
||||||
|
@internal.route("/postfix/sender/<sender>")
|
||||||
|
def postfix_sender(sender):
|
||||||
|
""" Simply reject any sender that pretends to be from a local domain
|
||||||
|
"""
|
||||||
|
localpart, domain_name = sender.split('@', 1) if '@' in sender else (None, sender)
|
||||||
|
domain = models.Domain.query.get(domain_name)
|
||||||
|
alternative = models.Alternative.query.get(domain_name)
|
||||||
|
if domain or alternative:
|
||||||
|
return flask.jsonify("REJECT")
|
||||||
|
return flask.abort(404)
|
@ -0,0 +1,28 @@
|
|||||||
|
""" Add a column for used quota
|
||||||
|
|
||||||
|
Revision ID: 25fd6c7bcb4a
|
||||||
|
Revises: 049fed905da7
|
||||||
|
Create Date: 2018-07-25 21:56:09.729153
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '25fd6c7bcb4a'
|
||||||
|
down_revision = '049fed905da7'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
with op.batch_alter_table('user') as batch:
|
||||||
|
batch.add_column(sa.Column('quota_bytes_used', sa.Integer(), nullable=False, server_default='0'))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
with op.batch_alter_table('user') as batch:
|
||||||
|
batch.drop_column('user', 'quota_bytes_used')
|
@ -1,14 +1,15 @@
|
|||||||
FROM alpine:3.7
|
FROM alpine:3.8
|
||||||
|
|
||||||
RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
|
RUN apk add --no-cache \
|
||||||
&& apk add --no-cache \
|
dovecot dovecot-pigeonhole-plugin dovecot-fts-lucene rspamd-client \
|
||||||
dovecot dovecot-sqlite dovecot-pigeonhole-plugin dovecot-pigeonhole-plugin-extdata \
|
python3 py3-pip \
|
||||||
rspamd-client@testing python py-jinja2
|
&& pip3 install --upgrade pip \
|
||||||
|
&& pip3 install jinja2 podop tenacity
|
||||||
|
|
||||||
COPY conf /conf
|
COPY conf /conf
|
||||||
COPY sieve /var/lib/dovecot
|
|
||||||
COPY start.py /start.py
|
COPY start.py /start.py
|
||||||
|
|
||||||
EXPOSE 110/tcp 143/tcp 993/tcp 4190/tcp 2525/tcp
|
EXPOSE 110/tcp 143/tcp 993/tcp 4190/tcp 2525/tcp
|
||||||
|
VOLUME ["/data", "/mail"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
@ -0,0 +1,5 @@
|
|||||||
|
uri = proxy:/tmp/podop.socket:auth
|
||||||
|
iterate_disable = yes
|
||||||
|
default_pass_scheme = plain
|
||||||
|
password_key = passdb/%u
|
||||||
|
user_key = userdb/%u
|
@ -1,18 +0,0 @@
|
|||||||
driver = sqlite
|
|
||||||
connect = /data/main.db
|
|
||||||
|
|
||||||
# Return the user hashed password
|
|
||||||
password_query = \
|
|
||||||
SELECT NULL as password, 'Y' as nopassword, '{% if POD_ADDRESS_RANGE %}{{ POD_ADDRESS_RANGE }}{% else %}{{ FRONT_ADDRESS }}{% if WEBMAIL_ADDRESS %},{{ WEBMAIL_ADDRESS }}{% endif %}{% endif %}' as allow_nets \
|
|
||||||
FROM user \
|
|
||||||
WHERE user.email = '%u'
|
|
||||||
|
|
||||||
# Mostly get the user quota
|
|
||||||
user_query = \
|
|
||||||
SELECT '*:bytes=' || user.quota_bytes AS quota_rule \
|
|
||||||
FROM user \
|
|
||||||
WHERE user.email = '%u'
|
|
||||||
|
|
||||||
# For using doveadm -A:
|
|
||||||
iterate_query = \
|
|
||||||
SELECT user.email AS user FROM user
|
|
@ -1,43 +0,0 @@
|
|||||||
connect = /data/main.db
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/spam_enabled
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = spam_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/spam_threshold
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = spam_threshold
|
|
||||||
}
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/reply_enabled
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = reply_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/reply_subject
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = reply_subject
|
|
||||||
}
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/reply_body
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = reply_body
|
|
||||||
}
|
|
||||||
|
|
||||||
map {
|
|
||||||
pattern = priv/reply_enddate
|
|
||||||
table = user
|
|
||||||
username_field = email
|
|
||||||
value_field = reply_enddate
|
|
||||||
}
|
|
@ -0,0 +1,11 @@
|
|||||||
|
require ["vnd.dovecot.execute", "copy", "imapsieve", "environment", "variables"];
|
||||||
|
|
||||||
|
if environment :matches "imap.mailbox" "*" {
|
||||||
|
set "mailbox" "${1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if string "${mailbox}" "Trash" {
|
||||||
|
stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute :pipe "mailtrain" "ham";
|
@ -1,3 +0,0 @@
|
|||||||
require "vnd.dovecot.execute";
|
|
||||||
|
|
||||||
execute :pipe "mailtrain" "ham";
|
|
@ -1,21 +1,40 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import glob
|
import glob
|
||||||
|
import multiprocessing
|
||||||
|
import tenacity
|
||||||
|
|
||||||
|
from tenacity import retry
|
||||||
|
from podop import run_server
|
||||||
|
|
||||||
|
|
||||||
|
def start_podop():
|
||||||
|
os.setuid(8)
|
||||||
|
run_server(3 if "DEBUG" in os.environ else 0, "dovecot", "/tmp/podop.socket", [
|
||||||
|
("quota", "url", "http://admin/internal/dovecot/§"),
|
||||||
|
("auth", "url", "http://admin/internal/dovecot/§"),
|
||||||
|
("sieve", "url", "http://admin/internal/dovecot/§"),
|
||||||
|
])
|
||||||
|
|
||||||
convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ))
|
convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ))
|
||||||
|
|
||||||
# Actual startup script
|
@retry(stop=tenacity.stop_after_attempt(100), wait=tenacity.wait_random(min=2, max=5))
|
||||||
os.environ["FRONT_ADDRESS"] = socket.gethostbyname(os.environ.get("FRONT_ADDRESS", "front"))
|
def resolve():
|
||||||
os.environ["REDIS_ADDRESS"] = socket.gethostbyname(os.environ.get("REDIS_ADDRESS", "redis"))
|
os.environ["FRONT_ADDRESS"] = socket.gethostbyname(os.environ.get("FRONT_ADDRESS", "front"))
|
||||||
if os.environ["WEBMAIL"] != "none":
|
os.environ["REDIS_ADDRESS"] = socket.gethostbyname(os.environ.get("REDIS_ADDRESS", "redis"))
|
||||||
|
if os.environ["WEBMAIL"] != "none":
|
||||||
os.environ["WEBMAIL_ADDRESS"] = socket.gethostbyname(os.environ.get("WEBMAIL_ADDRESS", "webmail"))
|
os.environ["WEBMAIL_ADDRESS"] = socket.gethostbyname(os.environ.get("WEBMAIL_ADDRESS", "webmail"))
|
||||||
|
|
||||||
for dovecot_file in glob.glob("/conf/*"):
|
# Actual startup script
|
||||||
|
resolve()
|
||||||
|
|
||||||
|
for dovecot_file in glob.glob("/conf/*.conf"):
|
||||||
convert(dovecot_file, os.path.join("/etc/dovecot", os.path.basename(dovecot_file)))
|
convert(dovecot_file, os.path.join("/etc/dovecot", os.path.basename(dovecot_file)))
|
||||||
|
|
||||||
# Run postfix
|
# Run Podop, then postfix
|
||||||
|
multiprocessing.Process(target=start_podop).start()
|
||||||
os.system("chown -R mail:mail /mail /var/lib/dovecot")
|
os.system("chown -R mail:mail /mail /var/lib/dovecot")
|
||||||
os.execv("/usr/sbin/dovecot", ["dovecot", "-c", "/etc/dovecot/dovecot.conf", "-F"])
|
os.execv("/usr/sbin/dovecot", ["dovecot", "-c", "/etc/dovecot/dovecot.conf", "-F"])
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
FROM alpine:3.7
|
FROM alpine:3.8
|
||||||
|
|
||||||
RUN apk add --no-cache nginx nginx-mod-mail python py-jinja2 certbot openssl
|
RUN apk add --no-cache certbot nginx nginx-mod-mail openssl \
|
||||||
|
python py-jinja2 py-requests-toolbelt py-pip \
|
||||||
|
&& pip install --upgrade pip \
|
||||||
|
&& pip install idna
|
||||||
|
|
||||||
COPY conf /conf
|
COPY conf /conf
|
||||||
COPY *.py /
|
COPY *.py /
|
||||||
|
|
||||||
EXPOSE 80/tcp 443/tcp 110/tcp 143/tcp 465/tcp 587/tcp 993/tcp 995/tcp 25/tcp 10025/tcp 10143/tcp
|
EXPOSE 80/tcp 443/tcp 110/tcp 143/tcp 465/tcp 587/tcp 993/tcp 995/tcp 25/tcp 10025/tcp 10143/tcp
|
||||||
|
VOLUME ["/certs"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
@ -1,5 +1,5 @@
|
|||||||
# This is an idle image to dynamically replace any component if disabled.
|
# This is an idle image to dynamically replace any component if disabled.
|
||||||
|
|
||||||
FROM alpine
|
FROM alpine:3.8
|
||||||
|
|
||||||
CMD sleep 1000000d
|
CMD sleep 1000000d
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
FROM alpine:3.7
|
FROM alpine:3.8
|
||||||
|
|
||||||
RUN apk add --no-cache postfix postfix-sqlite postfix-pcre rsyslog python py-jinja2
|
RUN apk add --no-cache postfix postfix-pcre rsyslog \
|
||||||
|
python3 py3-pip \
|
||||||
|
&& pip3 install --upgrade pip \
|
||||||
|
&& pip3 install jinja2 podop tenacity
|
||||||
|
|
||||||
COPY conf /conf
|
COPY conf /conf
|
||||||
COPY start.py /start.py
|
COPY start.py /start.py
|
||||||
|
|
||||||
EXPOSE 25/tcp 10025/tcp
|
EXPOSE 25/tcp 10025/tcp
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
CMD /start.py
|
CMD /start.py
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
dbpath = /data/main.db
|
|
||||||
query =
|
|
||||||
SELECT 'REJECT' FROM domain WHERE name='%s'
|
|
||||||
UNION
|
|
||||||
SELECT 'REJECT' FROM alternative WHERE name='%s'
|
|
@ -1,3 +0,0 @@
|
|||||||
dbpath = /data/main.db
|
|
||||||
query =
|
|
||||||
SELECT 'smtp:['||smtp||']' FROM relay WHERE name='%s'
|
|
@ -1,23 +0,0 @@
|
|||||||
dbpath = /data/main.db
|
|
||||||
query =
|
|
||||||
SELECT destination
|
|
||||||
FROM
|
|
||||||
(SELECT destination, email, wildcard, localpart, localpart||'@'||alternative.name AS alt_email FROM alias LEFT JOIN alternative ON alias.domain_name = alternative.domain_name
|
|
||||||
UNION
|
|
||||||
SELECT (CASE WHEN forward_enabled=1 THEN (CASE WHEN forward_keep=1 THEN email||',' ELSE '' END)||forward_destination ELSE email END) AS destination, email, 0 as wildcard, localpart, localpart||'@'||alternative.name as alt_email FROM user LEFT JOIN alternative ON user.domain_name = alternative.domain_name
|
|
||||||
UNION
|
|
||||||
SELECT '@'||domain_name as destination, '@'||name as email, 0 as wildcard, '' as localpart, NULL AS alt_email FROM alternative)
|
|
||||||
WHERE
|
|
||||||
(
|
|
||||||
wildcard = 0
|
|
||||||
AND
|
|
||||||
(email = '%s' OR alt_email = '%s')
|
|
||||||
) OR (
|
|
||||||
wildcard = 1
|
|
||||||
AND
|
|
||||||
'%s' LIKE email
|
|
||||||
)
|
|
||||||
ORDER BY
|
|
||||||
wildcard ASC,
|
|
||||||
length(localpart) DESC
|
|
||||||
LIMIT 1
|
|
@ -1,5 +0,0 @@
|
|||||||
dbpath = /data/main.db
|
|
||||||
query =
|
|
||||||
SELECT name FROM domain WHERE name='%s'
|
|
||||||
UNION
|
|
||||||
SELECT name FROM alternative WHERE name='%s'
|
|
@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3-alpine
|
||||||
|
|
||||||
|
COPY requirements.txt /requirements.txt
|
||||||
|
|
||||||
|
RUN pip install -r /requirements.txt \
|
||||||
|
&& apk add --no-cache nginx \
|
||||||
|
&& mkdir /run/nginx
|
||||||
|
|
||||||
|
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY . /docs
|
||||||
|
|
||||||
|
RUN sphinx-build /docs /build
|
||||||
|
|
||||||
|
CMD nginx -g "daemon off;"
|
@ -0,0 +1,157 @@
|
|||||||
|
# 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!
|
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-admin
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-admin
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: admin
|
||||||
|
image: mailu/admin:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
volumeMounts:
|
||||||
|
- name: maildata
|
||||||
|
mountPath: /data
|
||||||
|
subPath: maildata
|
||||||
|
- name: maildata
|
||||||
|
mountPath: /dkim
|
||||||
|
subPath: dkim
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 500Mi
|
||||||
|
cpu: 500m
|
||||||
|
limits:
|
||||||
|
memory: 500Mi
|
||||||
|
cpu: 500m
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: admin
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-admin
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-admin
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
@ -0,0 +1,153 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: mailu-config
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
data:
|
||||||
|
# Mailu main configuration file
|
||||||
|
#
|
||||||
|
# Most configuration variables can be modified through the Web interface,
|
||||||
|
# these few settings must however be configured before starting the mail
|
||||||
|
# server and require a restart upon change.
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Common configuration variables
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Set this to the path where Mailu data and configuration is stored
|
||||||
|
ROOT: "/mailu"
|
||||||
|
|
||||||
|
# Mailu version to run (1.0, 1.1, etc. or master)
|
||||||
|
VERSION: "master"
|
||||||
|
|
||||||
|
# Set to a randomly generated 16 bytes string
|
||||||
|
SECRET_KEY: "YourKeyHere"
|
||||||
|
|
||||||
|
# Address where listening ports should bind
|
||||||
|
BIND_ADDRESS4: "127.0.0.1"
|
||||||
|
#BIND_ADDRESS6: "::1"
|
||||||
|
|
||||||
|
# Main mail domain
|
||||||
|
DOMAIN: "example.com"
|
||||||
|
|
||||||
|
# Hostnames for this server, separated with comas
|
||||||
|
HOSTNAMES: "mail.example.com"
|
||||||
|
|
||||||
|
# Postmaster local part (will append the main mail domain)
|
||||||
|
POSTMASTER: "admin"
|
||||||
|
|
||||||
|
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
|
||||||
|
TLS_FLAVOR: "cert"
|
||||||
|
|
||||||
|
# Authentication rate limit (per source IP address)
|
||||||
|
AUTH_RATELIMIT: "10/minute;1000/hour"
|
||||||
|
|
||||||
|
# Opt-out of statistics, replace with "True" to opt out
|
||||||
|
DISABLE_STATISTICS: "False"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Optional features
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Expose the admin interface (value: true, false)
|
||||||
|
ADMIN: "true"
|
||||||
|
# Run the admin interface in debug mode
|
||||||
|
#DEBUG: "True"
|
||||||
|
|
||||||
|
# Choose which webmail to run if any (values: roundcube, rainloop, none)
|
||||||
|
WEBMAIL: "roundcube"
|
||||||
|
|
||||||
|
# Dav server implementation (value: radicale, none)
|
||||||
|
WEBDAV: "radicale"
|
||||||
|
|
||||||
|
# Antivirus solution (value: clamav, none)
|
||||||
|
ANTIVIRUS: "clamav"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Mail settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Message size limit in bytes
|
||||||
|
# Default: accept messages up to 50MB
|
||||||
|
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
|
||||||
|
#RELAYHOST=
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
# Fetchmail delay
|
||||||
|
FETCHMAIL_DELAY: "600"
|
||||||
|
|
||||||
|
# Recipient delimiter, character used to delimiter localpart from custom address part
|
||||||
|
# e.g. localpart+custom@domain;tld
|
||||||
|
RECIPIENT_DELIMITER: "+"
|
||||||
|
|
||||||
|
# DMARC rua and ruf email
|
||||||
|
DMARC_RUA: "root"
|
||||||
|
DMARC_RUF: "root"
|
||||||
|
|
||||||
|
# Welcome email, enable and set a topic and body if you wish to send welcome
|
||||||
|
# emails to all users.
|
||||||
|
WELCOME: "false"
|
||||||
|
WELCOME_SUBJECT: "Welcome to your new email account"
|
||||||
|
WELCOME_BODY: "Welcome to your new email account, if you can read this, then it is configured properly!"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Web settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Path to the admin interface if enabled
|
||||||
|
WEB_ADMIN: "/admin"
|
||||||
|
|
||||||
|
# Path to the webmail if enabled
|
||||||
|
WEB_WEBMAIL: "/webmail"
|
||||||
|
|
||||||
|
# Website name
|
||||||
|
SITENAME: "AppSynth"
|
||||||
|
|
||||||
|
# Linked Website URL
|
||||||
|
WEBSITE: "https://example.com"
|
||||||
|
|
||||||
|
# Registration reCaptcha settings (warning, this has some privacy impact)
|
||||||
|
# RECAPTCHA_PUBLIC_KEY=
|
||||||
|
# RECAPTCHA_PRIVATE_KEY=
|
||||||
|
|
||||||
|
# Domain registration, uncomment to enable
|
||||||
|
# DOMAIN_REGISTRATION=true
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Advanced settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Docker-compose project name, this will prepended to containers names.
|
||||||
|
COMPOSE_PROJECT_NAME: "mailu"
|
||||||
|
|
||||||
|
# Default password scheme used for newly created accounts and changed passwords
|
||||||
|
# (value: SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
|
||||||
|
PASSWORD_SCHEME: "SHA512-CRYPT"
|
||||||
|
|
||||||
|
# Header to take the real ip from
|
||||||
|
#REAL_IP_HEADER:
|
||||||
|
|
||||||
|
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
|
||||||
|
#REAL_IP_FROM:
|
||||||
|
|
||||||
|
# Host settings
|
||||||
|
HOST_IMAP: "imap.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_POP3: "imap.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_SMTP: "smtp.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_AUTHSMTP: "smtp.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_WEBMAIL: "webmail.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_ADMIN: "admin.mailu-mailserver.svc.cluster.local"
|
||||||
|
HOST_WEBDAV: "webdav.mailu-mailserver.svc.cluster.local:5232"
|
||||||
|
HOST_ANTISPAM: "antispam.mailu-mailserver.svc.cluster.local:11332"
|
||||||
|
HOST_REDIS: "redis.mailu-mailserver.svc.cluster.local"
|
@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-fetchmail
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-fetchmail
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: fetchmail
|
||||||
|
image: mailu/fetchmail:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
volumeMounts:
|
||||||
|
- name: maildata
|
||||||
|
mountPath: /data
|
||||||
|
subPath: maildata
|
||||||
|
ports:
|
||||||
|
- containerPort: 5232
|
||||||
|
- containerPort: 80
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
@ -0,0 +1,129 @@
|
|||||||
|
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-front
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-front
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
restartPolicy: Always
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- name: front
|
||||||
|
image: mailu/nginx:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
volumeMounts:
|
||||||
|
- name: certs
|
||||||
|
mountPath: /certs
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
- name: https
|
||||||
|
containerPort: 443
|
||||||
|
protocol: TCP
|
||||||
|
- name: pop3
|
||||||
|
containerPort: 110
|
||||||
|
protocol: TCP
|
||||||
|
- name: pop3s
|
||||||
|
containerPort: 995
|
||||||
|
protocol: TCP
|
||||||
|
- name: imap
|
||||||
|
containerPort: 143
|
||||||
|
protocol: TCP
|
||||||
|
- name: imaps
|
||||||
|
containerPort: 993
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp
|
||||||
|
containerPort: 25
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp-auth
|
||||||
|
containerPort: 10025
|
||||||
|
protocol: TCP
|
||||||
|
- name: imap-auth
|
||||||
|
containerPort: 10143
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtps
|
||||||
|
containerPort: 465
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtpd
|
||||||
|
containerPort: 587
|
||||||
|
protocol: TCP
|
||||||
|
- name: auth
|
||||||
|
containerPort: 8000
|
||||||
|
protocol: TCP
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 200Mi
|
||||||
|
cpu: 200m
|
||||||
|
volumes:
|
||||||
|
- 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: front
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-admin
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-front
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
protocol: TCP
|
||||||
|
- name: pop3
|
||||||
|
port: 110
|
||||||
|
protocol: TCP
|
||||||
|
- name: pop3s
|
||||||
|
port: 995
|
||||||
|
protocol: TCP
|
||||||
|
- name: imap
|
||||||
|
port: 143
|
||||||
|
protocol: TCP
|
||||||
|
- name: imaps
|
||||||
|
port: 993
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp
|
||||||
|
port: 25
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtps
|
||||||
|
port: 465
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtpd
|
||||||
|
port: 587
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp-auth
|
||||||
|
port: 10025
|
||||||
|
protocol: TCP
|
||||||
|
- name: imap-auth
|
||||||
|
port: 10143
|
||||||
|
protocol: TCP
|
@ -0,0 +1,80 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-imap
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-imap
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: imap
|
||||||
|
image: mailu/dovecot:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /data
|
||||||
|
name: maildata
|
||||||
|
subPath: maildata
|
||||||
|
- mountPath: /mail
|
||||||
|
name: maildata
|
||||||
|
subPath: mailstate
|
||||||
|
- mountPath: /overrides
|
||||||
|
name: maildata
|
||||||
|
subPath: overrides
|
||||||
|
ports:
|
||||||
|
- containerPort: 2102
|
||||||
|
- containerPort: 2525
|
||||||
|
- containerPort: 143
|
||||||
|
- containerPort: 993
|
||||||
|
- containerPort: 4190
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 500Mi
|
||||||
|
cpu: 500m
|
||||||
|
limits:
|
||||||
|
memory: 1Gi
|
||||||
|
cpu: 1000m
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: imap
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
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
|
@ -0,0 +1,32 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: mailu-ssl-ingress
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: tectonic
|
||||||
|
kubernetes.io/tls-acme: "true"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||||
|
ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
# Replace letsencrypt-prod with the name of the certificate issuer
|
||||||
|
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
|
||||||
|
#ingress.kubernetes.io/rewrite-target: "/"
|
||||||
|
#ingress.kubernetes.io/app-root: "/ui"
|
||||||
|
#ingress.kubernetes.io/follow-redirects: "true"
|
||||||
|
labels:
|
||||||
|
app: mailu
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- "mail.example.com"
|
||||||
|
secretName: letsencrypt-certs-all # If unsure how to generate these, check out https://github.com/ployst/docker-letsencrypt
|
||||||
|
rules:
|
||||||
|
- host: "mail.example.com"
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: "/"
|
||||||
|
backend:
|
||||||
|
serviceName: front
|
||||||
|
servicePort: 80
|
@ -0,0 +1,27 @@
|
|||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: redis-hdd
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
annotations:
|
||||||
|
volume.beta.kubernetes.io/storage-class: "glusterblock-hdd"
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: mail-storage
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
annotations:
|
||||||
|
volume.beta.kubernetes.io/storage-class: "gluster-heketi-hdd"
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 100Gi
|
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: mailu-mailserver
|
@ -0,0 +1,56 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-redis
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
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
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 200Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 300Mi
|
||||||
|
cpu: 200m
|
||||||
|
volumes:
|
||||||
|
- name: redisdata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: redis-hdd
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-redis
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-redis
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
- name: redis
|
||||||
|
port: 6379
|
||||||
|
protocol: TCP
|
@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-security
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-security
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: antispam
|
||||||
|
image: mailu/rspamd:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 200Mi
|
||||||
|
cpu: 200m
|
||||||
|
ports:
|
||||||
|
- name: antispam
|
||||||
|
containerPort: 11332
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: filter
|
||||||
|
subPath: filter
|
||||||
|
mountPath: /var/lib/rspamd
|
||||||
|
- name: filter
|
||||||
|
mountPath: /dkim
|
||||||
|
subPath: dkim
|
||||||
|
- name: filter
|
||||||
|
mountPath: /etc/rspamd/override.d
|
||||||
|
subPath: rspamd-overrides
|
||||||
|
- name: antivirus
|
||||||
|
image: mailu/clamav:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 1Gi
|
||||||
|
cpu: 1000m
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
cpu: 1000m
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
ports:
|
||||||
|
- name: antivirus
|
||||||
|
containerPort: 3310
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: filter
|
||||||
|
subPath: filter
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: filter
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: antispam
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-antispam
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-security
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
- name: antispam
|
||||||
|
port: 11332
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: antivirus
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-antivirus
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-security
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
- name: antivirus
|
||||||
|
port: 3310
|
||||||
|
protocol: TCP
|
@ -0,0 +1,80 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-smtp
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-smtp
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: smtp
|
||||||
|
image: mailu/postfix:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 500Mi
|
||||||
|
cpu: 200m
|
||||||
|
limits:
|
||||||
|
memory: 1Gi
|
||||||
|
cpu: 500m
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /data
|
||||||
|
name: maildata
|
||||||
|
subPath: maildata
|
||||||
|
- mountPath: /overrides
|
||||||
|
name: maildata
|
||||||
|
subPath: overrides
|
||||||
|
ports:
|
||||||
|
- name: smtp
|
||||||
|
containerPort: 25
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp-ssl
|
||||||
|
containerPort: 465
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp-starttls
|
||||||
|
containerPort: 587
|
||||||
|
protocol: TCP
|
||||||
|
- name: smtp-auth
|
||||||
|
containerPort: 10025
|
||||||
|
protocol: TCP
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: smtp
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
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
|
||||||
|
- name: smtp-auth
|
||||||
|
port: 10025
|
||||||
|
protocol: TCP
|
@ -0,0 +1,63 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-webdav
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-webdav
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: radicale
|
||||||
|
image: mailu/radicale:master
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /data
|
||||||
|
name: maildata
|
||||||
|
subPath: dav
|
||||||
|
ports:
|
||||||
|
- containerPort: 5232
|
||||||
|
- containerPort: 80
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: webdav
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-webdav
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-webdav
|
||||||
|
role: mail
|
||||||
|
tier: backend
|
||||||
|
ports:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
||||||
|
- name: http-ui
|
||||||
|
port: 5232
|
||||||
|
protocol: TCP
|
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mailu-roundcube
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mailu-roundcube
|
||||||
|
role: mail
|
||||||
|
tier: frontend
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: roundcube
|
||||||
|
image: mailu/roundcube:1.5
|
||||||
|
imagePullPolicy: Always
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mailu-config
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 100Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 200Mi
|
||||||
|
cpu: 200m
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /data
|
||||||
|
name: maildata
|
||||||
|
subPath: webmail
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
volumes:
|
||||||
|
- name: maildata
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mail-storage
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: webmail
|
||||||
|
namespace: mailu-mailserver
|
||||||
|
labels:
|
||||||
|
app: mailu-roundcube
|
||||||
|
role: mail
|
||||||
|
tier: frontend
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mailu-roundcube
|
||||||
|
role: mail
|
||||||
|
tier: frontend
|
||||||
|
ports:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
protocol: TCP
|
@ -0,0 +1,55 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: default-http-backend
|
||||||
|
labels:
|
||||||
|
app: default-http-backend
|
||||||
|
namespace: kube-ingress
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: default-http-backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: default-http-backend
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- name: default-http-backend
|
||||||
|
# Any image is permissible as long as:
|
||||||
|
# 1. It serves a 404 page at /
|
||||||
|
# 2. It serves 200 on a /healthz endpoint
|
||||||
|
image: gcr.io/google_containers/defaultbackend:1.4
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: default-http-backend
|
||||||
|
namespace: kube-ingress
|
||||||
|
labels:
|
||||||
|
app: default-http-backend
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: default-http-backend
|
@ -0,0 +1,139 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
# keep it under 24 chars
|
||||||
|
name: appsynth-lb
|
||||||
|
namespace: kube-ingress
|
||||||
|
labels:
|
||||||
|
k8s-app: appsynth-lb
|
||||||
|
component: ingress-controller
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
k8s-app: appsynth-lb
|
||||||
|
component: ingress-controller
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
- name: https
|
||||||
|
protocol: TCP
|
||||||
|
port: 443
|
||||||
|
targetPort: 443
|
||||||
|
---
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: udp-services
|
||||||
|
namespace: kube-ingress
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: tcp-services
|
||||||
|
namespace: kube-ingress
|
||||||
|
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
|
||||||
|
data:
|
||||||
|
enable-vts-status: "true"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: nginx-ingress-lb-conf
|
||||||
|
namespace: kube-ingress
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: ingress-controller
|
||||||
|
namespace: kube-ingress
|
||||||
|
annotations:
|
||||||
|
prometheus.io/port: "10254"
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
labels:
|
||||||
|
k8s-app: appsynth-lb
|
||||||
|
component: ingress-controller
|
||||||
|
type: nginx
|
||||||
|
spec:
|
||||||
|
updateStrategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxUnavailable: 1
|
||||||
|
type: RollingUpdate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: appsynth-lb
|
||||||
|
component: ingress-controller
|
||||||
|
type: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: appsynth-lb
|
||||||
|
component: ingress-controller
|
||||||
|
type: nginx
|
||||||
|
spec:
|
||||||
|
serviceAccount: kube-nginx-ingress
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: node-role.kubernetes.io/master
|
||||||
|
operator: DoesNotExist
|
||||||
|
containers:
|
||||||
|
- name: nginx-ingress-lb
|
||||||
|
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.16.2
|
||||||
|
args:
|
||||||
|
- /nginx-ingress-controller
|
||||||
|
- --configmap=$(POD_NAMESPACE)/tectonic-custom-error
|
||||||
|
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
||||||
|
#- --default-ssl-certificate=tectonic-system/tectonic-ingress-tls-secret
|
||||||
|
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
|
||||||
|
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
|
||||||
|
- --annotations-prefix=ingress.kubernetes.io
|
||||||
|
- --enable-ssl-passthrough
|
||||||
|
- --ingress-class=tectonic
|
||||||
|
# use downward API
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
hostPort: 80
|
||||||
|
- name: https
|
||||||
|
containerPort: 443
|
||||||
|
hostPort: 443
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
timeoutSeconds: 1
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
hostNetwork: true
|
||||||
|
nodeSelector:
|
||||||
|
node-role.kubernetes.io/node: ""
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
restartPolicy: Always
|
||||||
|
terminationGracePeriodSeconds: 60
|
@ -0,0 +1,129 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: kube-ingress
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
namespace: kube-ingress
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- endpoints
|
||||||
|
- nodes
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- "extensions"
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- events
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- "extensions"
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
namespace: kube-ingress
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- pods
|
||||||
|
- secrets
|
||||||
|
- namespaces
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
resourceNames:
|
||||||
|
- "ingress-controller-leader-nginx"
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
namespace: kube-ingress
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
namespace: kube-ingress
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: kube-nginx-ingress
|
||||||
|
namespace: kube-ingress
|
@ -0,0 +1,5 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
root /build;
|
||||||
|
}
|
@ -0,0 +1,364 @@
|
|||||||
|
# Install Mailu on a docker swarm
|
||||||
|
|
||||||
|
## Prequisites
|
||||||
|
|
||||||
|
### Swarm
|
||||||
|
|
||||||
|
In order to deploy Mailu on a swarm, you will first need to initialize the swarm:
|
||||||
|
|
||||||
|
The main command will be:
|
||||||
|
```bash
|
||||||
|
docker swarm init --advertise-addr <IP_ADDR>
|
||||||
|
```
|
||||||
|
See https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/
|
||||||
|
|
||||||
|
If you want to add other managers or workers, please use:
|
||||||
|
```bash
|
||||||
|
docker swarm join --token xxxxx
|
||||||
|
```
|
||||||
|
See https://docs.docker.com/engine/swarm/join-nodes/
|
||||||
|
|
||||||
|
You have now a working swarm, and you can check its status with:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~/git/Mailu/docs/swarm/1.5 $ docker node ls
|
||||||
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
|
||||||
|
xhgeekkrlttpmtgmapt5hyxrb black-pearl Ready Active 18.06.0-ce
|
||||||
|
sczlqjgfhehsfdjhfhhph1nvb * coreos-01 Ready Active Leader 18.03.1-ce
|
||||||
|
mzrm9nbdggsfz4sgq6dhs5i6n flying-dutchman Ready Active 18.06.0-ce
|
||||||
|
```
|
||||||
|
|
||||||
|
### Volume definition
|
||||||
|
For data persistance (the Mailu services might be launched/relaunched on any of the swarm nodes), we need to have Mailu data stored in a manner accessible by every manager or worker in the swarm.
|
||||||
|
Hereafter we will use a NFS share:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ showmount -e 192.168.0.30
|
||||||
|
Export list for 192.168.0.30:
|
||||||
|
/mnt/Pool1/pv 192.168.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
on the nfs server, I am using the following /etc/exports
|
||||||
|
```bash
|
||||||
|
$more /etc/exports
|
||||||
|
/mnt/Pool1/pv -alldirs -mapall=root -network 192.168.0.0 -mask 255.255.255.0
|
||||||
|
```
|
||||||
|
on the nfs server, I created the Mailu directory (in fact I copied a working Mailu set-up)
|
||||||
|
```bash
|
||||||
|
$mkdir /mnt/Pool1/pv/mailu
|
||||||
|
```
|
||||||
|
|
||||||
|
On your manager node, mount the nfs share to check that the share is available:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ sudo mount -t nfs 192.168.0.30:/mnt/Pool1/pv/mailu /mnt/local/
|
||||||
|
```
|
||||||
|
If this is ok, you can umount it:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ sudo umount /mnt/local/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Networking mode
|
||||||
|
On a swarm, the services are available (default mode) through a routing mesh managed by docker itself. With this mode, each service is given a virtual IP adress and docker manages the routing between this virtual IP and the container(s) provinding this service.
|
||||||
|
With this default networking mode, I cannot get login working properly... As found in https://github.com/Mailu/Mailu/issues/375 , a workaround is to use the dnsrr networking mode at least for the front services.
|
||||||
|
|
||||||
|
The main consequence/limitation will be that the front services will *not* be available on every node, but only on the node where it will be deployed. In my case, I have only one manager and I choose to deploy the front service to the manager node, so I know on wich IP the front service will be available (aka the IP adress of my manager node).
|
||||||
|
|
||||||
|
### Variable substitution and docker-compose.yml
|
||||||
|
The docker stack deploy command doesn't support variable substitution in the .yml file itself (but we still can use .env file to pass variables to the services). As a consequence we need to adjust the docker-compose file in order to :
|
||||||
|
- remove all variables : $VERSION , $BIND_ADDRESS4 , $BIND_ADDRESS6 , $ANTIVIRUS , $WEBMAIL , etc
|
||||||
|
- change the way we define the volumes (nfs share in our case)
|
||||||
|
- add a deploy section for every service
|
||||||
|
|
||||||
|
### Docker compose
|
||||||
|
An example of docker-compose-stack.yml file is available here:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
version: '3.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
front:
|
||||||
|
image: mailu/nginx:1.5
|
||||||
|
env_file: .env
|
||||||
|
ports:
|
||||||
|
- target: 80
|
||||||
|
published: 80
|
||||||
|
mode: host
|
||||||
|
- target: 443
|
||||||
|
published: 443
|
||||||
|
mode: host
|
||||||
|
- target: 110
|
||||||
|
published: 110
|
||||||
|
mode: host
|
||||||
|
- target: 143
|
||||||
|
published: 143
|
||||||
|
mode: host
|
||||||
|
- target: 993
|
||||||
|
published: 993
|
||||||
|
mode: host
|
||||||
|
- target: 995
|
||||||
|
published: 995
|
||||||
|
mode: host
|
||||||
|
- target: 25
|
||||||
|
published: 25
|
||||||
|
mode: host
|
||||||
|
- target: 465
|
||||||
|
published: 465
|
||||||
|
mode: host
|
||||||
|
- target: 587
|
||||||
|
published: 587
|
||||||
|
mode: host
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/certs:/certs"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_certs
|
||||||
|
target: /certs
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/redis:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_redis
|
||||||
|
target: /data
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
imap:
|
||||||
|
image: mailu/dovecot:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/data:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_data
|
||||||
|
target: /data
|
||||||
|
# - "$ROOT/mail:/mail"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_mail
|
||||||
|
target: /mail
|
||||||
|
# - "$ROOT/overrides:/overrides"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_overrides
|
||||||
|
target: /overrides
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
image: mailu/postfix:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/data:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_data
|
||||||
|
target: /data
|
||||||
|
# - "$ROOT/overrides:/overrides"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_overrides
|
||||||
|
target: /overrides
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
image: mailu/rspamd:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/filter:/var/lib/rspamd"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_filter
|
||||||
|
target: /var/lib/rspamd
|
||||||
|
# - "$ROOT/dkim:/dkim"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_dkim
|
||||||
|
target: /dkim
|
||||||
|
# - "$ROOT/overrides/rspamd:/etc/rspamd/override.d"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_overrides_rspamd
|
||||||
|
target: /etc/rspamd/override.d
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
antivirus:
|
||||||
|
image: mailu/none:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/filter:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_filter
|
||||||
|
target: /data
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
webdav:
|
||||||
|
image: mailu/none:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/dav:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_dav
|
||||||
|
target: /data
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
admin:
|
||||||
|
image: mailu/admin:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/data:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_data
|
||||||
|
target: /data
|
||||||
|
# - "$ROOT/dkim:/dkim"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_dkim
|
||||||
|
target: /dkim
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
webmail:
|
||||||
|
image: "mailu/roundcube:1.5"
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/webmail:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_data
|
||||||
|
target: /data
|
||||||
|
depends_on:
|
||||||
|
- imap
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
fetchmail:
|
||||||
|
image: mailu/fetchmail:1.5
|
||||||
|
restart: always
|
||||||
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
# - "$ROOT/data:/data"
|
||||||
|
- type: volume
|
||||||
|
source: mailu_data
|
||||||
|
target: /data
|
||||||
|
logging:
|
||||||
|
driver: none
|
||||||
|
deploy:
|
||||||
|
endpoint_mode: dnsrr
|
||||||
|
replicas: 1
|
||||||
|
placement:
|
||||||
|
constraints: [node.role == manager]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mailu_filter:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/filter"
|
||||||
|
mailu_dkim:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/dkim"
|
||||||
|
mailu_overrides_rspamd:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/overrides/rspamd"
|
||||||
|
mailu_data:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/data"
|
||||||
|
mailu_mail:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/mail"
|
||||||
|
mailu_overrides:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/overrides"
|
||||||
|
mailu_dav:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/dav"
|
||||||
|
mailu_certs:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/certs"
|
||||||
|
mailu_redis:
|
||||||
|
driver_opts:
|
||||||
|
type: "nfs"
|
||||||
|
o: "addr=192.168.0.30,nolock,soft,rw"
|
||||||
|
device: ":/mnt/Pool1/pv/mailu/redis"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy Mailu on the docker swarm
|
||||||
|
Run the following command:
|
||||||
|
```bash
|
||||||
|
docker stack deploy -c docker-compose-stack.yml mailu
|
||||||
|
```
|
||||||
|
See how the services are being deployed:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ docker service ls
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
ywnsetmtkb1l mailu_antivirus replicated 1/1 mailu/none:1.5
|
||||||
|
pqokiaz0q128 mailu_fetchmail replicated 1/1 mailu/fetchmail:1.5
|
||||||
|
```
|
||||||
|
check a specific service:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ docker service ps mailu_fetchmail
|
||||||
|
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||||
|
tbu8ppgsdffj mailu_fetchmail.1 mailu/fetchmail:1.5 coreos-01 Running Running 11 days ago
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remove the stack
|
||||||
|
Run the follwoing command:
|
||||||
|
```bash
|
||||||
|
core@coreos-01 ~ $ docker stack rm mailu
|
||||||
|
```
|
@ -1,7 +1,9 @@
|
|||||||
FROM python:alpine
|
FROM python:3-alpine
|
||||||
|
|
||||||
RUN apk add --no-cache fetchmail ca-certificates
|
RUN apk add --no-cache fetchmail ca-certificates \
|
||||||
|
&& pip install requests
|
||||||
|
|
||||||
COPY fetchmail.py /fetchmail.py
|
COPY fetchmail.py /fetchmail.py
|
||||||
|
USER fetchmail
|
||||||
|
|
||||||
CMD ["/fetchmail.py"]
|
CMD ["/fetchmail.py"]
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
try_fallback = true;
|
||||||
|
path = "/dkim/$domain.$selector.key";
|
||||||
|
selector = "dkim"
|
||||||
|
use_esld = false;
|
@ -0,0 +1,13 @@
|
|||||||
|
# This file is used to run the mailu/setup utility
|
||||||
|
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
|
||||||
|
setup:
|
||||||
|
image: mailu/setup
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
|
@ -0,0 +1,56 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
front:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/nginx:${VERSION:-local}
|
||||||
|
build: ../core/nginx
|
||||||
|
|
||||||
|
imap:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/dovecot:${VERSION:-local}
|
||||||
|
build: ../core/dovecot
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/postfix:${VERSION:-local}
|
||||||
|
build: ../core/postfix
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/rspamd:${VERSION:-local}
|
||||||
|
build: ../services/rspamd
|
||||||
|
|
||||||
|
antivirus:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/clamav:${VERSION:-local}
|
||||||
|
build: ../optional/clamav
|
||||||
|
|
||||||
|
webdav:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/radicale:${VERSION:-local}
|
||||||
|
build: ../optional/radicale
|
||||||
|
|
||||||
|
admin:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/admin:${VERSION:-local}
|
||||||
|
build: ../core/admin
|
||||||
|
|
||||||
|
roundcube:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/roundcube:${VERSION:-local}
|
||||||
|
build: ../webmails/roundcube
|
||||||
|
|
||||||
|
rainloop:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/rainloop:${VERSION:-local}
|
||||||
|
build: ../webmails/rainloop
|
||||||
|
|
||||||
|
fetchmail:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/fetchmail:${VERSION:-local}
|
||||||
|
build: ../services/fetchmail
|
||||||
|
|
||||||
|
none:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/none:${VERSION:-local}
|
||||||
|
build: ../core/none
|
||||||
|
|
||||||
|
docs:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/docs:${VERSION:-local}
|
||||||
|
build: ../docs
|
||||||
|
|
||||||
|
setup:
|
||||||
|
image: ${DOCKER_ORG:-mailu}/setup:${VERSION:-local}
|
||||||
|
build: ../setup
|
||||||
|
|
@ -0,0 +1,140 @@
|
|||||||
|
# Mailu main configuration file
|
||||||
|
#
|
||||||
|
# Most configuration variables can be modified through the Web interface,
|
||||||
|
# these few settings must however be configured before starting the mail
|
||||||
|
# server and require a restart upon change.
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Common configuration variables
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Set this to the path where Mailu data and configuration is stored
|
||||||
|
ROOT=/mailu
|
||||||
|
|
||||||
|
# Mailu version to run (1.0, 1.1, etc. or master)
|
||||||
|
#VERSION=master
|
||||||
|
|
||||||
|
# Set to a randomly generated 16 bytes string
|
||||||
|
SECRET_KEY=ChangeMeChangeMe
|
||||||
|
|
||||||
|
# Address where listening ports should bind
|
||||||
|
BIND_ADDRESS4=127.0.0.1
|
||||||
|
#BIND_ADDRESS6=::1
|
||||||
|
|
||||||
|
# Main mail domain
|
||||||
|
DOMAIN=mailu.io
|
||||||
|
|
||||||
|
# Hostnames for this server, separated with comas
|
||||||
|
HOSTNAMES=mail.mailu.io,alternative.mailu.io,yetanother.mailu.io
|
||||||
|
|
||||||
|
# Postmaster local part (will append the main mail domain)
|
||||||
|
POSTMASTER=admin
|
||||||
|
|
||||||
|
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
|
||||||
|
TLS_FLAVOR=cert
|
||||||
|
|
||||||
|
# Authentication rate limit (per source IP address)
|
||||||
|
AUTH_RATELIMIT=10/minute;1000/hour
|
||||||
|
|
||||||
|
# Opt-out of statistics, replace with "True" to opt out
|
||||||
|
DISABLE_STATISTICS=False
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Optional features
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Expose the admin interface (value: true, false)
|
||||||
|
ADMIN=false
|
||||||
|
|
||||||
|
# Choose which webmail to run if any (values: roundcube, rainloop, none)
|
||||||
|
WEBMAIL=none
|
||||||
|
|
||||||
|
# Dav server implementation (value: radicale, none)
|
||||||
|
WEBDAV=none
|
||||||
|
|
||||||
|
# Antivirus solution (value: clamav, none)
|
||||||
|
ANTIVIRUS=none
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Mail settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Message size limit in bytes
|
||||||
|
# Default: accept messages up to 50MB
|
||||||
|
MESSAGE_SIZE_LIMIT=50000000
|
||||||
|
|
||||||
|
# Networks granted relay permissions, make sure that you include your Docker
|
||||||
|
# internal network (default to 172.17.0.0/16)
|
||||||
|
RELAYNETS=172.16.0.0/12
|
||||||
|
|
||||||
|
# Will relay all outgoing mails if configured
|
||||||
|
RELAYHOST=
|
||||||
|
|
||||||
|
# Fetchmail delay
|
||||||
|
FETCHMAIL_DELAY=600
|
||||||
|
|
||||||
|
# Recipient delimiter, character used to delimiter localpart from custom address part
|
||||||
|
# e.g. localpart+custom@domain;tld
|
||||||
|
RECIPIENT_DELIMITER=+
|
||||||
|
|
||||||
|
# DMARC rua and ruf email
|
||||||
|
DMARC_RUA=admin
|
||||||
|
DMARC_RUF=admin
|
||||||
|
|
||||||
|
# Welcome email, enable and set a topic and body if you wish to send welcome
|
||||||
|
# emails to all users.
|
||||||
|
WELCOME=false
|
||||||
|
WELCOME_SUBJECT=Welcome to your new email account
|
||||||
|
WELCOME_BODY=Welcome to your new email account, if you can read this, then it is configured properly!
|
||||||
|
|
||||||
|
# Maildir Compression
|
||||||
|
# choose compression-method, default: none (value: bz2, gz)
|
||||||
|
COMPRESSION=
|
||||||
|
# change compression-level, default: 6 (value: 1-9)
|
||||||
|
COMPRESSION_LEVEL=
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Registration reCaptcha settings (warning, this has some privacy impact)
|
||||||
|
# RECAPTCHA_PUBLIC_KEY=
|
||||||
|
# RECAPTCHA_PRIVATE_KEY=
|
||||||
|
|
||||||
|
# Domain registration, uncomment to enable
|
||||||
|
# DOMAIN_REGISTRATION=true
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Advanced settings
|
||||||
|
###################################
|
||||||
|
|
||||||
|
# Log driver for front service. Possible values:
|
||||||
|
# json-file (default)
|
||||||
|
# journald (On systemd platforms, useful for Fail2Ban integration)
|
||||||
|
# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker-compose log` for front!)
|
||||||
|
LOG_DRIVER=json-file
|
||||||
|
|
||||||
|
# Docker-compose project name, this will prepended to containers names.
|
||||||
|
#COMPOSE_PROJECT_NAME=mailu
|
||||||
|
|
||||||
|
# Default password scheme used for newly created accounts and changed passwords
|
||||||
|
# (value: SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
|
||||||
|
PASSWORD_SCHEME=SHA512-CRYPT
|
||||||
|
|
||||||
|
# Header to take the real ip from
|
||||||
|
REAL_IP_HEADER=
|
||||||
|
|
||||||
|
# IPs for nginx set_real_ip_from (CIDR list separated by commas)
|
||||||
|
REAL_IP_FROM=
|
@ -0,0 +1,101 @@
|
|||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
front:
|
||||||
|
image: $DOCKER_ORG/nginx:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
logging:
|
||||||
|
driver: $LOG_DRIVER
|
||||||
|
ports:
|
||||||
|
- "$BIND_ADDRESS4:80:80"
|
||||||
|
- "$BIND_ADDRESS4:443:443"
|
||||||
|
- "$BIND_ADDRESS4:110:110"
|
||||||
|
- "$BIND_ADDRESS4:143:143"
|
||||||
|
- "$BIND_ADDRESS4:993:993"
|
||||||
|
- "$BIND_ADDRESS4:995:995"
|
||||||
|
- "$BIND_ADDRESS4:25:25"
|
||||||
|
- "$BIND_ADDRESS4:465:465"
|
||||||
|
- "$BIND_ADDRESS4:587:587"
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/certs:/certs"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
restart: 'no'
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/redis:/data"
|
||||||
|
|
||||||
|
imap:
|
||||||
|
image: $DOCKER_ORG/dovecot:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/data:/data"
|
||||||
|
- "$ROOT/mail:/mail"
|
||||||
|
- "$ROOT/overrides:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
smtp:
|
||||||
|
image: $DOCKER_ORG/postfix:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/data:/data"
|
||||||
|
- "$ROOT/overrides:/overrides"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
antispam:
|
||||||
|
image: $DOCKER_ORG/rspamd:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/filter:/var/lib/rspamd"
|
||||||
|
- "$ROOT/dkim:/dkim"
|
||||||
|
- "$ROOT/overrides/rspamd:/etc/rspamd/override.d"
|
||||||
|
depends_on:
|
||||||
|
- front
|
||||||
|
|
||||||
|
antivirus:
|
||||||
|
image: $DOCKER_ORG/$ANTIVIRUS:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/filter:/data"
|
||||||
|
|
||||||
|
webdav:
|
||||||
|
image: $DOCKER_ORG/$WEBDAV:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/dav:/data"
|
||||||
|
|
||||||
|
admin:
|
||||||
|
image: $DOCKER_ORG/admin:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/data:/data"
|
||||||
|
- "$ROOT/dkim:/dkim"
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
webmail:
|
||||||
|
image: "$DOCKER_ORG/$WEBMAIL:$VERSION"
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/webmail:/data"
|
||||||
|
depends_on:
|
||||||
|
- imap
|
||||||
|
|
||||||
|
fetchmail:
|
||||||
|
image: $DOCKER_ORG/fetchmail:$VERSION
|
||||||
|
restart: 'no'
|
||||||
|
env_file: $PWD/.env
|
||||||
|
volumes:
|
||||||
|
- "$ROOT/data:/data"
|
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
containers=(
|
||||||
|
webmail
|
||||||
|
imap
|
||||||
|
smtp
|
||||||
|
antispam
|
||||||
|
admin
|
||||||
|
redis
|
||||||
|
antivirus
|
||||||
|
webdav
|
||||||
|
# fetchmail
|
||||||
|
front
|
||||||
|
)
|
||||||
|
|
||||||
|
# Time to sleep in minutes after starting the containers
|
||||||
|
WAIT=1
|
||||||
|
|
||||||
|
containers_check() {
|
||||||
|
status=0
|
||||||
|
for container in "${containers[@]}"; do
|
||||||
|
name="${DOCKER_ORG}_${container}_1"
|
||||||
|
echo "Checking $name"
|
||||||
|
docker inspect "$name" | grep '"Status": "running"' || status=1
|
||||||
|
done
|
||||||
|
docker ps -a
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
container_logs() {
|
||||||
|
for container in "${containers[@]}"; do
|
||||||
|
name="${DOCKER_ORG}_${container}_1"
|
||||||
|
echo "Showing logs for $name"
|
||||||
|
docker container logs "$name"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
docker-compose -f tests/compose/run.yml -p $DOCKER_ORG down || exit 1
|
||||||
|
rm -fv .env
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup before callig exit
|
||||||
|
die() {
|
||||||
|
clean
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in tests/compose/*.env ; do
|
||||||
|
cp $file .env
|
||||||
|
docker-compose -f tests/compose/run.yml -p $DOCKER_ORG up -d
|
||||||
|
echo -e "\nSleeping for ${WAIT} minutes" # Clean terminal distortion from docker-compose in travis
|
||||||
|
travis_wait sleep ${WAIT}m || sleep ${WAIT}m #Fallback sleep for local run
|
||||||
|
container_logs
|
||||||
|
containers_check || die 1
|
||||||
|
clean
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
docker login -u $DOCKER_UN -p $DOCKER_PW
|
||||||
|
docker-compose -f tests/build.yml push
|
@ -0,0 +1,17 @@
|
|||||||
|
import smtplib
|
||||||
|
import sys
|
||||||
|
from email import mime
|
||||||
|
|
||||||
|
from email.mime.image import MIMEImage
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
|
msg = mime.multipart.MIMEMultipart()
|
||||||
|
msg['Subject'] = 'Test email'
|
||||||
|
msg['From'] = sys.argv[1]
|
||||||
|
msg['To'] = sys.argv[2]
|
||||||
|
msg.preamble = 'Test email'
|
||||||
|
|
||||||
|
s = smtplib.SMTP('localhost')
|
||||||
|
s.set_debuglevel(1)
|
||||||
|
s.send_message(msg)
|
||||||
|
s.quit()
|
Loading…
Reference in New Issue