Merge branch 'master' into feat-image-prefix

master
hoellen 6 years ago committed by GitHub
commit 02367d1447
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,14 +4,30 @@ addons:
apt:
packages:
- docker-ce
env:
- VERSION=$TRAVIS_BRANCH
- MAILU_VERSION=$TRAVIS_BRANCH
language: python
python:
- "3.6"
install:
- pip install -r tests/requirements.txt
- sudo curl -L https://github.com/docker/compose/releases/download/1.23.0-rc3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
before_script:
- docker-compose -v
- docker-compose -f tests/build.yml build
- sudo -- sh -c 'mkdir -p /mailu && cp -r tests/certs /mailu && chmod 600 /mailu/certs/*'
script:
# Default to mailu for DOCKER_ORG
- if [ -z "$DOCKER_ORG" ]; then export DOCKER_ORG="mailu"; fi
- docker-compose -f tests/build.yml build
- tests/compose/test-script.sh
# test.py, test name and timeout between start and tests.
- python tests/compose/test.py core 1
- python tests/compose/test.py fetchmail 1
- travis_wait python tests/compose/test.py filters 10
- python tests/compose/test.py rainloop 1
- python tests/compose/test.py roundcube 1
- python tests/compose/test.py webdav 1
deploy:
provider: script
@ -19,4 +35,3 @@ deploy:
on:
all_branches: true
condition: -n $DOCKER_UN

@ -25,4 +25,4 @@ VOLUME ["/data"]
CMD /start.py
HEALTHCHECK CMD curl -f -L http://localhost/ui || exit 1
HEALTHCHECK CMD curl -f -L http://localhost/ui/login?next=ui.index || exit 1

@ -6,7 +6,9 @@ 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)
domain = models.Domain.query.get(domain_name) or \
models.Alternative.query.get(domain_name) or \
flask.abort(404)
return flask.jsonify(domain.name)
@ -18,37 +20,34 @@ def postfix_mailbox_map(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)
localpart, domain_name = models.Email.resolve_domain(alias)
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)
return flask.jsonify(domain_name)
destination = models.Email.resolve_destination(localpart, domain_name)
return flask.jsonify(",".join(destination)) if destination else 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)
if email == '*':
return flask.abort(404)
localpart, domain_name = models.Email.resolve_domain(email)
relay = models.Relay.query.get(domain_name) or flask.abort(404)
return flask.jsonify("smtp:[{}]".format(relay.smtp))
@internal.route("/postfix/sender/<sender>")
def postfix_sender(sender):
@internal.route("/postfix/sender/login/<sender>")
def postfix_sender_login(sender):
localpart, domain_name = models.Email.resolve_domain(sender)
if localpart is None:
return flask.abort(404)
destination = models.Email.resolve_destination(localpart, domain_name, True)
return flask.jsonify(",".join(destination)) if destination else flask.abort(404)
@internal.route("/postfix/sender/access/<sender>")
def postfix_sender_access(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)
localpart, domain_name = models.Email.resolve_domain(sender)
return flask.jsonify("REJECT") if models.Domain.query.get(domain_name) else flask.abort(404)

@ -35,11 +35,14 @@ class IdnaEmail(db.TypeDecorator):
impl = db.String(255, collation="NOCASE")
def process_bind_param(self, value, dialect):
localpart, domain_name = value.split('@')
return "{0}@{1}".format(
localpart,
idna.encode(domain_name).decode('ascii'),
)
try:
localpart, domain_name = value.split('@')
return "{0}@{1}".format(
localpart,
idna.encode(domain_name).decode('ascii'),
)
except ValueError:
pass
def process_result_value(self, value, dialect):
localpart, domain_name = value.split('@')
@ -64,7 +67,7 @@ class CommaSeparatedList(db.TypeDecorator):
return ",".join(value)
def process_result_value(self, value, dialect):
return filter(bool, value.split(","))
return filter(bool, value.split(",")) if value else []
# Many-to-many association table for domain managers
@ -221,6 +224,28 @@ class Email(object):
msg['To'] = to_address
smtp.sendmail(from_address, [to_address], msg.as_string())
@classmethod
def resolve_domain(cls, email):
localpart, domain_name = email.split('@', 1) if '@' in email else (None, email)
alternative = Alternative.query.get(domain_name)
if alternative:
domain_name = alternative.domain_name
return (localpart, domain_name)
@classmethod
def resolve_destination(cls, localpart, domain_name, ignore_forward_keep=False):
alias = Alias.resolve(localpart, domain_name)
if alias:
return alias.destination
user = User.query.get('{}@{}'.format(localpart, domain_name))
if user:
if user.forward_enabled:
destination = user.forward_destination
if user.forward_keep or ignore_forward_keep:
destination.append(user.email)
else:
destination = [user.email]
return destination
def __str__(self):
return self.email
@ -245,7 +270,7 @@ class User(Base, Email):
# Filters
forward_enabled = db.Column(db.Boolean(), nullable=False, default=False)
forward_destination = db.Column(db.String(255), nullable=True, default=None)
forward_destination = db.Column(CommaSeparatedList(), nullable=True, default=[])
forward_keep = db.Column(db.Boolean(), nullable=False, default=True)
reply_enabled = db.Column(db.Boolean(), nullable=False, default=False)
reply_subject = db.Column(db.String(255), nullable=True, default=None)

@ -90,9 +90,10 @@ class UserSignupForm(flask_wtf.FlaskForm):
localpart = fields.StringField(_('Email address'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])
pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
captcha = flask_wtf.RecaptchaField()
submit = fields.SubmitField(_('Sign up'))
class UserSignupFormCaptcha(UserSignupForm):
captcha = flask_wtf.RecaptchaField()
class UserSettingsForm(flask_wtf.FlaskForm):
displayed_name = fields.StringField(_('Displayed name'))

@ -14,7 +14,9 @@
{% call macros.box() %}
{{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }}
{{ macros.form_fields((form.pw, form.pw2)) }}
{{ macros.form_field(form.captcha) }}
{% if form.captcha %}
{{ macros.form_field(form.captcha) }}
{% endif %}
{{ macros.form_field(form.submit) }}
{% endcall %}
</form>

@ -170,7 +170,11 @@ def user_signup(domain_name=None):
available_domains=available_domains)
domain = available_domains.get(domain_name) or flask.abort(404)
quota_bytes = domain.max_quota_bytes or app.config['DEFAULT_QUOTA']
form = forms.UserSignupForm()
if app.config['RECAPTCHA_PUBLIC_KEY'] == "" or app.config['RECAPTCHA_PRIVATE_KEY'] == "":
form = forms.UserSignupForm()
else:
form = forms.UserSignupFormCaptcha()
if form.validate_on_submit():
if domain.has_email(form.localpart.data):
flask.flash('Email is already used', 'error')

@ -78,14 +78,14 @@ lmtp_host_lookup = native
smtpd_delay_reject = yes
# Allowed senders are: the user or one of the alias destinations
smtpd_sender_login_maps = $virtual_alias_maps
smtpd_sender_login_maps = ${podop}senderlogin
# Restrictions for incoming SMTP, other restrictions are applied in master.cf
smtpd_helo_required = yes
smtpd_client_restrictions =
permit_mynetworks,
check_sender_access ${podop}sender,
check_sender_access ${podop}senderaccess,
reject_non_fqdn_sender,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,

@ -19,7 +19,8 @@ def start_podop():
("alias", "url", "http://admin/internal/postfix/alias/§"),
("domain", "url", "http://admin/internal/postfix/domain/§"),
("mailbox", "url", "http://admin/internal/postfix/mailbox/§"),
("sender", "url", "http://admin/internal/postfix/sender/§")
("senderaccess", "url", "http://admin/internal/postfix/sender/access/§"),
("senderlogin", "url", "http://admin/internal/postfix/sender/login/§")
])
convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ))

@ -1,3 +1,4 @@
try_fallback = true;
path = "/dkim/$domain.$selector.key";
use_esld = false;
allow_username_mismatch = true;

@ -12,6 +12,7 @@ COPY setup.py ./setup.py
COPY main.py ./main.py
COPY flavors /data/master/flavors
COPY templates /data/master/templates
COPY static ./static
#RUN python setup.py https://github.com/mailu/mailu /data

@ -0,0 +1,59 @@
## Adding more flavors/steps
(Everything will go under setup/ directory - using Kubernetes flavor as example)
Until this point, the app is working as it follows:
- when accesing the setup page it will display the flavors selection step (`templates/steps/flavor.html`)
- after you choose your desired flavor it will iterare over the files in the flavor directory and building the page
(`templates/steps/config.html is general for all flavors`)
- when you complete all required fields and press "Setup Mailu" button it will redirect you to the setup page (`flavors/choosen-flavor/setup.html`)
To add a new flavor you need to create a directory under `templates/steps/` in which you are adding actual steps.
Eg: Adding a WIP step we'll create `templates/steps/kubernetes/wip.html`
*Note that wizard.html is iterating over files in this directory and building the page. Files are prefixed with a number for sorting purposes.*
wip.html will start with
```
{% call macros.panel("info", "Step X - Work in progress") %}
```
and end with
```
{% endcall %}
```
You store variable from front-page using the name attribute inside tag.
In the example below the string entered in the input field is stored in the variable `named var_test`
```
<input type="text" name="var_test">
```
In order to user the variable furter you use it like `{{ var_test }}`
In the setup page (`flavors/kubernetes/setup.html`) you cand add steps by importing macros
```
{% import "macros.html" as macros %}
```
and start and end every step with
```
{% call macros.panel("info", "Step X - Title") %}
-------------------
{% endcall %}
```
### Generating a file
Create the file template in `flavors/kubernetes/` (eg. file.txt) in which you save your variables
```
ROOT = {{ root }}
MY_VAR = {{ var_test }}
```
When you submit to Setup Mailu the file will be generated. In order to get the file add the following command to setup.html
```
<p>curl {{ url_for('.file', uid=uid, filepath='file.txt', _external=True) }} > file.txt</p>
```

@ -1,13 +1,13 @@
# This file is used to run the mailu/setup utility
version: '2'
version: '3.6'
services:
redis:
image: redis:alpine
setup:
image: mailu/setup
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}setup:${MAILU_VERSION:-master}
ports:
- "8000:80"
build: .

@ -10,13 +10,17 @@ services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "{{ root }}/redis:/data"
# Core services
front:
image: mailu/nginx:{{ version }}
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
logging:
driver: {{ log_driver or 'json-file' }}
ports:
{% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %}
{% if bind4 %}
@ -40,7 +44,8 @@ services:
{% endif %}
admin:
image: mailu/admin:{{ version }}
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
{% if not admin_enabled %}
ports:
@ -53,7 +58,8 @@ services:
- redis
imap:
image: mailu/dovecot:{{ version }}
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/mail:/mail"
@ -62,7 +68,8 @@ services:
- front
smtp:
image: mailu/postfix:{{ version }}
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/overrides:/overrides"
@ -74,10 +81,9 @@ services:
- {{ dns }}
{% endif %}
# Optional services
{% if antispam_enabled %}
antispam:
image: mailu/rspamd:{{ version }}
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/filter:/var/lib/rspamd"
@ -90,11 +96,12 @@ services:
dns:
- {{ dns }}
{% endif %}
{% endif %}
# Optional services
{% if antivirus_enabled %}
antivirus:
image: mailu/clamav:{{ version }}
image: ${DOCKER_ORG:-mailu}/clamav:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/filter:/data"
@ -108,7 +115,8 @@ services:
{% if webdav_enabled %}
webdav:
image: mailu/radicale:{{ version }}
image: ${DOCKER_ORG:-mailu}/radicale:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/dav:/data"
@ -116,7 +124,8 @@ services:
{% if fetchmail_enabled %}
fetchmail:
image: mailu/fetchmail:{{ version }}
image: ${DOCKER_ORG:-mailu}/fetchmail:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
{% if resolver_enabled %}
depends_on:
@ -129,7 +138,8 @@ services:
# Webmail
{% if webmail_type != 'none' %}
webmail:
image: mailu/{{ webmail_type }}:{{ version }}
image: ${DOCKER_ORG:-mailu}/{{ webmail_type }}:${MAILU_VERSION:-{{ version }}}
restart: always
env_file: {{ env }}
volumes:
- "{{ root }}/webmail:/data"

@ -141,7 +141,7 @@ DOMAIN_REGISTRATION=true
# 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={{ log_driver or 'json-file' }}
# 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' }}

@ -28,15 +28,15 @@ files before going any further.</p>
{% call macros.panel("info", "Step 3 - Start the Compose project") %}
<p>To start your compose project, simply run the Docker Compose <code>up</code>
command.</p>
command using <code>-p mailu</code> flag for project name.</p>
<pre><code>cd {{ root }}
docker-compose up -d
docker-compose -p mailu up -d
</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>docker-compose -p mailu 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

@ -10,14 +10,15 @@ services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "{{ root }}/redis:/data"
# Core services
front:
image: mailu/nginx:{{ version }}
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
logging:
driver: {{ log_driver or 'json-file' }}
ports:
{% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %}
- target: {{ port }}
@ -27,7 +28,7 @@ services:
volumes:
- "{{ root }}/certs:/certs"
deploy:
replicas: 1
replicas: {{ front_replicas }}
{% if resolver_enabled %}
resolver:
@ -39,7 +40,7 @@ services:
{% endif %}
admin:
image: mailu/admin:{{ version }}
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
{% if not admin_enabled %}
ports:
@ -49,10 +50,10 @@ services:
- "{{ root }}/data:/data"
- "{{ root }}/dkim:/dkim"
deploy:
replicas: 1
replicas: {{ admin_replicas }}
imap:
image: mailu/dovecot:{{ version }}
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
environment:
# Default to 10.0.1.0/24
@ -61,26 +62,24 @@ services:
- "{{ root }}/mail:/mail"
- "{{ root }}/overrides:/overrides"
deploy:
replicas: 1
replicas: {{ imap_replicas }}
smtp:
image: mailu/postfix:{{ version }}
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
environment:
- POD_ADDRESS_RANGE={{ subnet }}
volumes:
- "{{ root }}/overrides:/overrides"
deploy:
replicas: 1
replicas: {{ smtp_replicas }}
{% if resolver_enabled %}
dns:
- {{ dns }}
{% endif %}
# Optional services
{% if antispam_enabled %}
antispam:
image: mailu/rspamd:{{ version }}
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
environment:
- POD_ADDRESS_RANGE={{ subnet }}
@ -94,11 +93,11 @@ services:
dns:
- {{ dns }}
{% endif %}
{% endif %}
# Optional services
{% if antivirus_enabled %}
antivirus:
image: mailu/clamav:{{ version }}
image: ${DOCKER_ORG:-mailu}/clamav:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
volumes:
- "{{ root }}/filter:/data"
@ -112,7 +111,7 @@ services:
{% if webdav_enabled %}
webdav:
image: mailu/none:{{ version }}
image: ${DOCKER_ORG:-mailu}/none:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
volumes:
- "{{ root }}/dav:/data"
@ -122,7 +121,7 @@ services:
{% if fetchmail_enabled %}
fetchmail:
image: mailu/fetchmail:{{ version }}
image: ${DOCKER_ORG:-mailu}/fetchmail:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
volumes:
- "{{ root }}/data:/data"
@ -136,7 +135,7 @@ services:
{% if webmail_type != 'none' %}
webmail:
image: mailu/roundcube:{{ version }}
image: ${DOCKER_ORG:-mailu}/roundcube:${MAILU_VERSION:-{{ version }}}
env_file: {{ env }}
volumes:
- "{{ root }}/webmail:/data"

@ -0,0 +1,34 @@
$(document).ready(function() {
if ($("#webmail").val() == 'none') {
$("#webmail_path").hide();
$("#webmail_path").attr("value", "");
} else {
$("#webmail_path").show();
$("#webmail_path").attr("value", "/webmail");
}
$("#webmail").click(function() {
if (this.value == 'none') {
$("#webmail_path").hide();
$("#webmail_path").attr("value", "");
} else {
$("#webmail_path").show();
$("#webmail_path").attr("value", "/webmail");
}
});
});
$(document).ready(function() {
if ($('#admin').prop('checked')) {
$("#admin_path").show();
$("#admin_path").attr("value", "/admin");
}
$("#admin").change(function() {
if ($(this).is(":checked")) {
$("#admin_path").show();
$("#admin_path").attr("value", "/admin");
} else {
$("#admin_path").hide();
$("#admin_path").attr("value", "");
}
});
});

@ -15,15 +15,14 @@ accessing messages for beginner users.</p>
<!-- {{ 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">
<select class="btn btn-primary dropdown-toggle" name="webmail_type" id="webmail">
{% 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">
<input class="form-control" type="text" name="webmail_path" id="webmail_path" style="display: none">
</div>
</div>
@ -32,12 +31,6 @@ 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">
@ -59,4 +52,9 @@ also disable the antivirus if required (it does use aroung 1GB of ram).</p>
</label>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='render.js') }}"></script>
{% endcall %}

@ -68,11 +68,13 @@ Or in plain english: if receivers start to classify your mail as spam, this post
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>
<input type="checkbox" name="admin_enabled" value="true" id="admin">
<label>Enable the admin UI (and path to the admin UI)</label>
<input class="form-control" type="text" name="admin_path" id="admin_path" style="display: none">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='render.js') }}"></script>
{% endcall %}

@ -15,15 +15,14 @@ accessing messages for beginner users.</p>
<!-- {{ 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">
<select class="btn btn-primary dropdown-toggle" name="webmail_type" id="webmail">
{% 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">
<input class="form-control" type="text" name="webmail_path" id="webmail_path" style="display: none">
</div>
</div>
@ -32,12 +31,6 @@ 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">
@ -59,4 +52,8 @@ also disable the antivirus if required (it does use aroung 1GB of ram).</p>
</label>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='render.js') }}"></script>
{% endcall %}

@ -0,0 +1,28 @@
{% call macros.panel("info", "Step 5 - Number of replicas for containers") %}
<p>Select number of replicas for containers</p>
<div class="form-group">
<input class="form-control" type="number" name="front_replicas" min="1" required value="1"
style="width: 6%; display: inline;">
<label>Front</label>
</div>
<div class="form-group">
<input class="form-control" type="number" name="admin_replicas" min="1" required value="1"
style="width: 6%; display: inline;">
<label>Admin</label>
</div>
<div class="form-group">
<input class="form-control" type="number" name="imap_replicas" min="1" required value="1"
style="width: 6%; display: inline;">
<label>IMAP</label>
</div>
<div class="form-group">
<input class="form-control" type="number" name=smtp_replicas min="1" required value="1"
style="width: 6%; display: inline;">
<label>SMPT</label>
</div>
{% endcall %}

@ -3,58 +3,58 @@ version: '3'
services:
front:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}nginx:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}nginx:${MAILU_VERSION:-local}
build: ../core/nginx
resolver:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}unbound:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}unbound:${MAILU_VERSION:-local}
build: ../services/unbound
imap:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}dovecot:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}dovecot:${MAILU_VERSION:-local}
build: ../core/dovecot
smtp:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}postfix:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}postfix:${MAILU_VERSION:-local}
build: ../core/postfix
antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}rspamd:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}rspamd:${MAILU_VERSION:-local}
build: ../services/rspamd
antivirus:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}clamav:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}clamav:${MAILU_VERSION:-local}
build: ../optional/clamav
webdav:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}radicale:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}radicale:${MAILU_VERSION:-local}
build: ../optional/radicale
admin:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}admin:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}admin:${MAILU_VERSION:-local}
build: ../core/admin
roundcube:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}roundcube:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}roundcube:${MAILU_VERSION:-local}
build: ../webmails/roundcube
rainloop:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}rainloop:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}rainloop:${MAILU_VERSION:-local}
build: ../webmails/rainloop
fetchmail:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}fetchmail:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}fetchmail:${MAILU_VERSION:-local}
build: ../services/fetchmail
none:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}none:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}none:${MAILU_VERSION:-local}
build: ../core/none
docs:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}docs:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}docs:${MAILU_VERSION:-local}
build: ../docs
setup:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}setup:${VERSION:-local}
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX}setup:${MAILU_VERSION:-local}
build: ../setup

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE/jCCAuagAwIBAgIJAKVnyadXS7SuMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xODEwMzExMDE1MzFaFw0yODEwMjgxMDE1MzFaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBAOQ2ZDqR+YvW5FKykBXz/Ec+jSb0Lv7GYQkT5t+TB1NXuR+QH1LfNWFmXOo7
YXcPVXlmcuLDuUldrctdS59fx8dnFu5gRRUqJwZuEQICypsX0rTDtsV6xqZB8c8y
2+BztP9OHfPpZdnU1IBx2fDbjpdKUaoAMFMFvyTaEcIyp6aGAhejvJCwc3D8fIJI
NhWA2O11sZQHUs7/MHzpu/IHpgutgk8EsNOUNLwB3+9p3IlOlTT6GilIXOYeTzoD
hiI6B5BQqXHsRrkao3v0YL6Ekun4hOx3MYx09AZtmuyrlq1mkNueKS5JwKDrXXbq
Ta0oyJ18UTZFRwVqApcuR4CA8vuhI9PsoDCvBQH1rW6FyiM4bhybatFJAYjQAODe
gwh2p6JWux5C1gaBUubOrKO7o5ePI6s0MmK8ZxrL4PpBYt3B33ztFfjWmVbCTSvP
GuQ2Ux73OY2NNxx2aNt4Th0IxrvMdsGLrZsdma2rWa5eTJTAuqbSjI/Wb1zjO0pi
pwoxk6f1COFLopo2xgJj6+KKG1nKLfOzQFexcpdq/mpuulcVcLDPJzJTLX3qsgtD
iBpm1ozNRT+M7XUavg8aHNfn6S+TcDb5hp+1yZ6obZq/VlA6atk0fuPzf+ndQ0fq
YN1jlAIzZXt/Dpc+ObjS09WGDVQXobGesdwA6BH14OV+TxOHAgMBAAGjUzBRMB0G
A1UdDgQWBBQy7kA8FbdcFpVU1AoFgzE7Fw1QqDAfBgNVHSMEGDAWgBQy7kA8Fbdc
FpVU1AoFgzE7Fw1QqDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IC
AQBLFlQKxztxm7MtsHs01Pl8/FpKzWekWK1ksf15d8mHBT30OTs+NXaJDuHTGL4r
rPeFf3NZ1PZkGRnJCEWur+8e8Y5KwuMAaagneSYXU0gcZfvTidvf865Jiml8xO5x
PAo8qTZQCHmYcvJQwBXMkq/2sFJCYeMOLoJdXXbTTe2ZQ/N3MSQbpgWJ8pF7srKU
biw2RkNH39QPq9GpWRQGx2gwvZDy2oFG8cM1hJYmz0Y9clpBE0mSqypvA1E8ufKC
uaUc0tpPI5H4efeWv/ObnFAJ3DMEmzUnQ8hdM/7cpf6AL8VRm4Wrw112gK7SbSdd
mMsUfFIDfyE9vsZ3OC8C8LqXKLwMcm7Fdq0ym0NINtoVW0ukmVJzB78CdWaJ7ux1
WqitcnewgiMWuuwuepBmNurZtgDrg+zgMhNpuK0NzYyE+ZReoJIOJCub3SSEsWdl
x5aJEYuFYJR5EvmxWeYv5p1GVOTL1TJqW7iRodzRoMc9u2vb0+tCbM5XSZVPul6P
QimDui2Ogq0zYNbSkHaUGBpjGDvHYG0zXO2sWrdrAJQMHo8dGEe7FuSuAlWbQdb/
xgN4uwejxV6B2e6rjT6YMni+r5Qw0EhNka+Xohw5E68bEcQSrCP8j64qQLAeipuz
ImqBTNyyR4WTcL+1HIVM7ZIw3igHH55zo5qTvyjKyZX9Uw==
-----END CERTIFICATE-----

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
W3079UVfBB4hkfN2721fqyj+r/0z+R0=
-----END PRIVATE KEY-----

@ -0,0 +1,4 @@
echo "Creating users ..."
docker-compose -f tests/compose/core/docker-compose.yml exec admin python3 manage.py admin admin mailu.io password || exit 1
docker-compose -f tests/compose/core/docker-compose.yml exec admin python3 manage.py user --hash_scheme='SHA512-CRYPT' user mailu.io 'password' || exit 1
echo "Admin and user successfully created!"

@ -0,0 +1 @@
python3 tests/email_test.py message-core

@ -0,0 +1,80 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
# Webmail

@ -1,31 +1,35 @@
# 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.
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# 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=/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
SECRET_KEY=HGZCYGVI6FVG31HS
# Address where listening ports should bind
BIND_ADDRESS4=127.0.0.1
#BIND_ADDRESS6=::1
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=mail.mailu.io,alternative.mailu.io,yetanother.mailu.io
HOSTNAMES=localhost
# Postmaster local part (will append the main mail domain)
POSTMASTER=admin
@ -34,7 +38,7 @@ POSTMASTER=admin
TLS_FLAVOR=cert
# Authentication rate limit (per source IP address)
AUTH_RATELIMIT=10/minute;1000/hour
AUTH_RATELIMIT=10/minute;1000/hour
# Opt-out of statistics, replace with "True" to opt out
DISABLE_STATISTICS=False
@ -44,7 +48,7 @@ DISABLE_STATISTICS=False
###################################
# Expose the admin interface (value: true, false)
ADMIN=false
ADMIN=true
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=none
@ -53,7 +57,10 @@ WEBMAIL=none
WEBDAV=none
# Antivirus solution (value: clamav, none)
ANTIVIRUS=none
#ANTIVIRUS=none
#Antispam solution
ANTISPAM=none
###################################
# Mail settings
@ -65,7 +72,7 @@ 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
RELAYNETS=172.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
@ -74,18 +81,12 @@ RELAYHOST=
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)
@ -109,12 +110,7 @@ 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
@ -124,17 +120,20 @@ WEBSITE=https://mailu.io
# 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
# LOG_DRIVER=json-file
# Docker-compose project name, this will prepended to containers names.
#COMPOSE_PROJECT_NAME=mailu
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
# (value: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -0,0 +1,84 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
fetchmail:
image: ${DOCKER_ORG:-mailu}/fetchmail:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
# Webmail

@ -0,0 +1,139 @@
# Mailu main configuration file
#
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set this to the path where Mailu data and configuration is stored
# This variable is now set directly in `docker-compose.yml by the setup utility
# 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=JS48Q9KE3B6T97E6
# Address where listening ports should bind
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=localhost
# 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
# 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
#Antispam solution
ANTISPAM=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.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# 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
###################################
# 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: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -0,0 +1,6 @@
python3 tests/email_test.py message-virus "tests/compose/filters/eicar.com"
if [ $? -eq 99 ]; then
exit 0
else
exit 1
fi

@ -0,0 +1,86 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
antivirus:
image: ${DOCKER_ORG:-mailu}/clamav:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/data"
# Webmail

@ -0,0 +1 @@
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

@ -0,0 +1,139 @@
# Mailu main configuration file
#
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set this to the path where Mailu data and configuration is stored
# This variable is now set directly in `docker-compose.yml by the setup utility
# 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=11H6XURLGE7GW3U1
# Address where listening ports should bind
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=localhost
# 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
# 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=clamav
#Antispam solution
ANTISPAM=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.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# 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
###################################
# 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: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -0,0 +1,88 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
# Webmail
webmail:
image: ${DOCKER_ORG:-mailu}/rainloop:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/webmail:/data"
depends_on:
- imap

@ -0,0 +1,139 @@
# Mailu main configuration file
#
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set this to the path where Mailu data and configuration is stored
# This variable is now set directly in `docker-compose.yml by the setup utility
# 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=V5J4SHRYVW9PZIQU
# Address where listening ports should bind
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=localhost
# 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
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=rainloop
# Dav server implementation (value: radicale, none)
WEBDAV=none
# Antivirus solution (value: clamav, none)
#ANTIVIRUS=none
#Antispam solution
ANTISPAM=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.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# 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
###################################
# 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: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -0,0 +1,88 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
# Webmail
webmail:
image: ${DOCKER_ORG:-mailu}/roundcube:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/webmail:/data"
depends_on:
- imap

@ -0,0 +1,139 @@
# Mailu main configuration file
#
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set this to the path where Mailu data and configuration is stored
# This variable is now set directly in `docker-compose.yml by the setup utility
# 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=PGGO2JRQ59QV3DW7
# Address where listening ports should bind
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=localhost
# 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
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=roundcube
# Dav server implementation (value: radicale, none)
WEBDAV=none
# Antivirus solution (value: clamav, none)
#ANTIVIRUS=none
#Antispam solution
ANTISPAM=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.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# 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
###################################
# 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: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -1,101 +0,0 @@
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"

@ -1,57 +0,0 @@
#!/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,100 @@
import sys
import os
import time
import docker
from colorama import Fore, Style
import subprocess
# Declare variables for service name and sleep time
test_name=sys.argv[1]
timeout=int(sys.argv[2])
test_path="tests/compose/" + test_name + "/"
compose_file=test_path + "docker-compose.yml"
client = docker.APIClient(base_url='unix://var/run/docker.sock')
containers = []
# Stop containers
def stop(exit_code):
print_logs()
sys.stdout.flush()
print(subprocess.check_output("docker-compose -f " + compose_file + " down", shell=True).decode())
sys.exit(exit_code)
# Sleep for a defined amount of time
def sleep():
print(Fore.LIGHTMAGENTA_EX + "Sleeping for " + str(timeout) + "m" + Style.RESET_ALL)
time.sleep(timeout*60)
def health_checks():
exit_code = 0
#Iterating trough all containers dictionary
for container in client.containers(all=True):
#Perform "docker container inspect" on container based on container ID and save output to a dictionary
container_inspect = client.inspect_container(container['Id']) #Dict
if "Health" in container_inspect['State'].keys():
if container_inspect['State']['Health']['Status'] == "healthy":
print(Fore.GREEN + "Health status for " + container_inspect['Name'].replace("/", "") + " : " + Fore.CYAN + container_inspect['State']['Health']['Status'] + Style.RESET_ALL)
if container_inspect['State']['Health']['Status'] != "healthy":
print(Fore.RED + "Container " + container_inspect['Name'].replace("/", "") + " is " + Fore.YELLOW + container_inspect['State']['Health']['Status']
+ Fore.RED + ", FailingStreak: " + Fore.YELLOW + str(container_inspect['State']['Health']['FailingStreak'])
+ Fore.RED + ", Log: " + Fore.YELLOW + str(container_inspect['State']['Health']['Log']) + Style.RESET_ALL)
exit_code = 1
else:
if container_inspect['State']['Status'] == "running":
print(Fore.GREEN + "Running status for " + container_inspect['Name'].replace("/", "") + " : " + Fore.BLUE + container_inspect['State']['Status'] + Style.RESET_ALL)
if container_inspect['State']['Status'] != "running":
print(Fore.RED + "Container " + container_inspect['Name'].replace("/", "") + " state is: " + Fore.YELLOW + container_inspect['State']['Status'] + Style.RESET_ALL)
exit_code = 1
#Saving Id, Name and state to a new dictionary
containers_dict = {}
containers_dict['Name'] = container_inspect['Name'].replace("/", "")
containers_dict['Id'] = container_inspect['Id']
containers_dict['State'] = container_inspect['State']
#Adding the generated dictionary to a list
containers.append(containers_dict)
if exit_code != 0:
stop(exit_code)
def print_logs():
print("Printing logs ...")
#Iterating through docker container inspect list and print logs
for container in containers:
print(Fore.LIGHTMAGENTA_EX + "Printing logs for: " + Fore.GREEN + container['Name'] + Style.RESET_ALL)
sys.stdout.flush()
print(subprocess.check_output('docker container logs ' + container['Name'], shell=True).decode())
#Iterating over hooks in test folder and running them
def hooks():
print(Fore.LIGHTMAGENTA_EX + "Running hooks" + Style.RESET_ALL)
for test_file in sorted(os.listdir(test_path)):
try:
if test_file.endswith(".py"):
sys.stdout.flush()
print(subprocess.check_output("python3 " + test_path + test_file, shell=True).decode())
elif test_file.endswith(".sh"):
sys.stdout.flush()
print(subprocess.check_output("./" + test_path + test_file, shell=True).decode())
except subprocess.CalledProcessError as e:
sys.stderr.write("[ERROR]: output = %s, error code = %s\n" % (e.output.decode(), e.returncode))
stop(1)
# Start up containers
sys.stdout.flush()
print(subprocess.check_output("docker-compose -f " + compose_file + " up -d", shell=True).decode())
print()
sleep()
print()
sys.stdout.flush()
print(subprocess.check_output("docker ps -a", shell=True).decode())
print()
health_checks()
print()
hooks()
print()
stop(0)

@ -0,0 +1,86 @@
# This file is auto-generated by the Mailu configuration wizard.
# Please read the documentation before attempting any change.
# Generated for compose flavor
version: '3.6'
services:
# External dependencies
redis:
image: redis:alpine
restart: always
volumes:
- "/mailu/redis:/data"
# Core services
front:
image: ${DOCKER_ORG:-mailu}/nginx:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
logging:
driver: json-file
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "127.0.0.1:25:25"
- "127.0.0.1:465:465"
- "127.0.0.1:587:587"
- "127.0.0.1:110:110"
- "127.0.0.1:995:995"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
volumes:
- "/mailu/certs:/certs"
admin:
image: ${DOCKER_ORG:-mailu}/admin:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/data:/data"
- "/mailu/dkim:/dkim"
depends_on:
- redis
imap:
image: ${DOCKER_ORG:-mailu}/dovecot:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/mail:/mail"
- "/mailu/overrides:/overrides"
depends_on:
- front
smtp:
image: ${DOCKER_ORG:-mailu}/postfix:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/overrides:/overrides"
depends_on:
- front
antispam:
image: ${DOCKER_ORG:-mailu}/rspamd:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim"
- "/mailu/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
# Optional services
webdav:
image: ${DOCKER_ORG:-mailu}/radicale:${MAILU_VERSION:-master}
restart: always
env_file: mailu.env
volumes:
- "/mailu/dav:/data"
# Webmail

@ -0,0 +1,139 @@
# Mailu main configuration file
#
# Generated for compose flavor
#
# This file is autogenerated by the configuration management wizard.
# For a detailed list of configuration variables, see the documentation at
# https://mailu.io
###################################
# Common configuration variables
###################################
# Set this to the path where Mailu data and configuration is stored
# This variable is now set directly in `docker-compose.yml by the setup utility
# 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=XVDDSWOAGVF5J9QJ
# Address where listening ports should bind
# This variables are now set directly in `docker-compose.yml by the setup utility
# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1)
# PUBLIC_IPV6= (default: ::1)
# Main mail domain
DOMAIN=mailu.io
# Hostnames for this server, separated with comas
HOSTNAMES=localhost
# 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
# Choose which webmail to run if any (values: roundcube, rainloop, none)
WEBMAIL=none
# Dav server implementation (value: radicale, none)
WEBDAV=radicale
# Antivirus solution (value: clamav, none)
#ANTIVIRUS=none
#Antispam solution
ANTISPAM=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.17.0.0/16
# Will relay all outgoing mails if configured
RELAYHOST=
# Fetchmail delay
FETCHMAIL_DELAY=600
# Recipient delimiter, character used to delimiter localpart from custom address part
RECIPIENT_DELIMITER=+
# DMARC rua and ruf email
DMARC_RUA=admin
DMARC_RUF=admin
# 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
###################################
# 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: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT)
PASSWORD_SCHEME=BLF-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=
# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no)
REJECT_UNLISTED_RECIPIENT=

@ -0,0 +1,59 @@
import smtplib
import imaplib
import time
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import ntpath
from email.mime.base import MIMEBase
from email import encoders
msg = MIMEMultipart()
msg['From'] = "admin@mailu.io"
msg['To'] = "user@mailu.io"
msg['Subject'] = "File Test"
msg.attach(MIMEText(sys.argv[1], 'plain'))
if len(sys.argv) == 3:
part = MIMEBase('application', 'octet-stream')
part.set_payload((open(sys.argv[2], "rb")).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename=%s" % ntpath.basename(sys.argv[2]))
msg.attach(part)
try:
smtp_server = smtplib.SMTP('localhost')
smtp_server.set_debuglevel(1)
smtp_server.connect('localhost', 587)
smtp_server.ehlo()
smtp_server.starttls()
smtp_server.ehlo()
smtp_server.login("admin@mailu.io", "password")
smtp_server.sendmail("admin@mailu.io", "user@mailu.io", msg.as_string())
smtp_server.quit()
except:
sys.exit(25)
time.sleep(30)
try:
imap_server = imaplib.IMAP4_SSL('localhost')
imap_server.login('user@mailu.io', 'password')
except:
sys.exit(110)
stat, count = imap_server.select('inbox')
try:
stat, data = imap_server.fetch(count[0], '(UID BODY[TEXT])')
except :
sys.exit(99)
if sys.argv[1] in str(data[0][1]):
print("Success sending and receiving email!")
else:
print("Failed receiving email with message %s" % sys.argv[1])
sys.exit(99)
imap_server.close()
imap_server.logout()

@ -0,0 +1,2 @@
docker
colorama
Loading…
Cancel
Save