diff --git a/setup/Dockerfile b/setup/Dockerfile index 1fc808f1..c970e57d 100644 --- a/setup/Dockerfile +++ b/setup/Dockerfile @@ -10,8 +10,10 @@ RUN apk add --no-cache git \ COPY server.py ./server.py COPY setup.py ./setup.py COPY main.py ./main.py +COPY flavors /data/master/flavors +COPY templates /data/master/templates -RUN python setup.py https://github.com/mailu/mailu /data +#RUN python setup.py https://github.com/mailu/mailu /data EXPOSE 80/tcp diff --git a/setup/docker-compose.yml b/setup/docker-compose.yml index 9288bb7e..e91332e1 100644 --- a/setup/docker-compose.yml +++ b/setup/docker-compose.yml @@ -9,5 +9,6 @@ services: setup: image: mailu/setup ports: - - "80:80" + - "8000:80" + build: . diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index fcf0c092..b01bb8fd 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -1,124 +1,106 @@ {% set env='mailu.env' %} # This file is auto-generated by the Mailu configuration wizard. # Please read the documentation before attempting any change. +# Generated for {{ flavor }} flavor -version: '2' +version: '3.6' services: # External dependencies redis: image: redis:alpine - restart: always volumes: - - "$ROOT/redis:/data" + - "{{ root }}/redis:/data" # Core services front: image: mailu/nginx:{{ version }} - restart: always env_file: {{ env }} - env: - - TLS_FLAVOR={{ tls_flavor or 'letsencrypt' }} - - ADMIN={{ expose_admin or 'no' }} ports: {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} {% if bind4 %} - - "$PUBLIC_IPV4:{{ port }}:{{ port }}" + - "{{ bind4 }}:{{ port }}:{{ port }}" {% endif %} {% if bind6 %} - - "$PUBLIC_IPV6:{{ port }}:{{ port }}" + - "{{ bind6 }}:{{ port }}:{{ port }}" {% endif %} {% endfor %} - {% if flavor in ('cert', 'mail') %} volumes: - - "$ROOT/certs:/certs" - {% endif %} + - "{{ root }}/certs:/certs" admin: image: mailu/admin:{{ version }} - restart: always env_file: {{ env }} - {% if not expose_admin %} + {% if not admin_enabled %} ports: - 127.0.0.1:8080:80 {% endif %} volumes: - - "$ROOT/data:/data" - - "$ROOT/dkim:/dkim" + - "{{ root }}/data:/data" + - "{{ root }}/dkim:/dkim" depends_on: - redis imap: image: mailu/dovecot:{{ version }} - restart: always env_file: {{ env }} volumes: - - "$ROOT/data:/data" - - "$ROOT/mail:/mail" - - "$ROOT/overrides:/overrides" + - "{{ root }}/mail:/mail" + - "{{ root }}/overrides:/overrides" depends_on: - front smtp: image: mailu/postfix:{{ version }} - restart: always env_file: {{ env }} volumes: - - "$ROOT/data:/data" - - "$ROOT/overrides:/overrides" + - "{{ root }}/overrides:/overrides" depends_on: - front # Optional services - {% if enable_antispam %} + {% if antispam_enabled %} antispam: image: mailu/rspamd:{{ version }} - restart: always env_file: {{ env }} volumes: - - "$ROOT/filter:/var/lib/rspamd" - - "$ROOT/dkim:/dkim" - - "$ROOT/overrides/rspamd:/etc/rspamd/override.d" + - "{{ root }}/filter:/var/lib/rspamd" + - "{{ root }}/dkim:/dkim" + - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d" depends_on: - front {% endif %} - {% if enable_antivirus %} + {% if antivirus_enabled %} antivirus: image: mailu/clamav:{{ version }} - restart: always env_file: {{ env }} volumes: - - "$ROOT/filter:/data" + - "{{ root }}/filter:/data" {% endif %} - {% if enable_webdav %} + {% if webdav_enabled %} webdav: - image: mailu/radivale:{{ version }} - restart: always + image: mailu/radicale:{{ version }} env_file: {{ env }} volumes: - - "$ROOT/dav:/data" + - "{{ root }}/dav:/data" {% endif %} - {% if enable_fetchmail %} + {% if fetchmail_enabled %} fetchmail: image: mailu/fetchmail:{{ version }} - restart: always env_file: {{ env }} - volumes: - - "$ROOT/data:/data" {% endif %} # Webmail - {% if enable_webmail %} + {% if webmail_type != 'none' %} webmail: - image: mailu/{{ webmail }}:{{ version }} - restart: always + image: mailu/{{ webmail_type }}:{{ version }} env_file: {{ env }} volumes: - - "$ROOT/webmail:/data" + - "{{ root }}/webmail:/data" depends_on: - imap {% endif %} diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env index 24d7b247..9fc1197d 100644 --- a/setup/flavors/compose/mailu.env +++ b/setup/flavors/compose/mailu.env @@ -1,5 +1,7 @@ # Mailu main configuration file # +# Generated for {{ flavor }} flavor +# # This file is autogenerated by the configuration management wizard. # For a detailed list of configuration variables, see the documentation at # https://mailu.io @@ -9,60 +11,118 @@ ################################### # Set this to the path where Mailu data and configuration is stored -ROOT=/mailu +# This variable is now set directly in `docker-compose.yml by the setup utility +# ROOT={{ root }} + +# Mailu version to run (1.0, 1.1, etc. or master) +#VERSION={{ version }} # Set to a randomly generated 16 bytes string SECRET_KEY={{ secret(16) }} # Address where listening ports should bind -{% if bind4 %}PUBLIC_IPV4={{ bind4 }}{% endif %} -{% if bind6 %}PUBLIC_IPV6={{ bind6 }}{% endif %} +# This variables are now set directly in `docker-compose.yml by the setup utility +# PUBLIC_IPV4= {{ bind4 }} (default: 127.0.0.1) +# PUBLIC_IPV6= {{ bind6 }} (default: ::1) -# Mail address of the postmaster -POSTMASTER={{ postmaster }} +# Main mail domain +DOMAIN={{ domain }} # Hostnames for this server, separated with comas HOSTNAMES={{ hostnames }} +# Postmaster local part (will append the main mail domain) +POSTMASTER={{ postmaster }} + +# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt) +TLS_FLAVOR={{ tls_flavor }} + # Authentication rate limit (per source IP address) -AUTH_RATELIMIT={{ auth_ratelimit }} +{% if auth_ratelimit_pm > '0' and auth_ratelimit_ph > '0' %} +AUTH_RATELIMIT={{ auth_ratelimit_pm }}/minute;{{ auth_ratelimit_ph }}/hour +{% endif %} # Opt-out of statistics, replace with "True" to opt out -DISABLE_STATISTICS={{ disable_statistics }} +DISABLE_STATISTICS={{ disable_statistics or 'False' }} ################################### -# Server behavior +# Optional features +################################### + +# Expose the admin interface (value: true, false) +ADMIN={{ admin_enabled or 'false' }} + +# Choose which webmail to run if any (values: roundcube, rainloop, none) +WEBMAIL={{ webmail_type }} + +# Dav server implementation (value: radicale, none) +WEBDAV={{ webdav_enabled or 'none' }} + +# Antivirus solution (value: clamav, none) +#ANTIVIRUS={{ antivirus_enabled or 'none' }} + +#Antispam solution +ANTISPAM={{ antispam_enabled or 'none'}} + +################################### +# Mail settings ################################### # Message size limit in bytes # Default: accept messages up to 50MB -MESSAGE_SIZE_LIMIT={{ message_size_limit }} +MESSAGE_SIZE_LIMIT={{ message_size_limit or '50000000' }} # Networks granted relay permissions, make sure that you include your Docker # internal network (default to 172.17.0.0/16) -RELAYNETS={{ relaynets }} +RELAYNETS={{ relaynets or '172.17.0.0/16' }} # Will relay all outgoing mails if configured RELAYHOST={{ relayhost }} # Fetchmail delay -FETCHMAIL_DELAY={{ fetchmail_delay }} +FETCHMAIL_DELAY={{ fetchmail_delay or '600' }} # Recipient delimiter, character used to delimiter localpart from custom address part -RECIPIENT_DELIMITER={{ recipient_delimiter }} +RECIPIENT_DELIMITER={{ recipient_delimiter or '+' }} -{% if dmarc_rua or dmarc_ruf %} # DMARC rua and ruf email -{% if dmarc_rua %}DMARC_RUA={{ dmarc_rua }}{% endif %} -{% if dmarc_ruf %}DMARC_RUF={{ dmarc_ruf }}{% endif %} -{% endif %} +DMARC_RUA={{ dmarc_rua or 'admin' }} +DMARC_RUF={{ dmarc_ruf or 'admin' }} {% if welcome_enabled %} # Welcome email, enable and set a topic and body if you wish to send welcome # emails to all users. -WELCOME={{ welcome_enable }} -WELCOME_SUBJECT={{ welcome_subject }} -WELCOME_BODY={{ welcome_body }} +WELCOME={{ welcome_enable or 'false' }} +WELCOME_SUBJECT={{ welcome_subject or 'Welcome to your new email account' }} +WELCOME_BODY={{ welcome_body or 'Welcome to your new email account, if you can read this, then it is configured properly!' }} +{% endif %} + +# Maildir Compression +# choose compression-method, default: none (value: bz2, gz) +COMPRESSION={{ compression }} +# change compression-level, default: 6 (value: 1-9) +COMPRESSION_LEVEL={{ compression_level }} + +################################### +# Web settings +################################### + +# Path to the admin interface if enabled +WEB_ADMIN={{ admin_path }} + +# Path to the webmail if enabled +WEB_WEBMAIL={{ webmail_path }} + +# Website name +SITENAME={{ site_name }} + +# Linked Website URL +WEBSITE={{ website }} + +{% if recaptcha_public_key and recaptcha_private_key %} +# Registration reCaptcha settings (warning, this has some privacy impact) +# RECAPTCHA_PUBLIC_KEY={{ recaptcha_public_key }} +# RECAPTCHA_PRIVATE_KEY={{ recaptcha_private_key }} {% endif %} {% if domain_registration %} @@ -70,39 +130,28 @@ WELCOME_BODY={{ welcome_body }} DOMAIN_REGISTRATION=true {% endif %} -################################### -# Web settings -################################### - -# Path to the admin interface if enabled -WEB_ADMIN=/admin - -# Path to the webmail if enabled -WEB_WEBMAIL=/webmail - -# Website name -SITENAME=Mailu - -# Linked Website URL -WEBSITE=https://mailu.io - -{% if recaptcha_public_key and recaptcha_private_key %} -# Registration reCaptcha settings (warning, this has some privacy impact) -# RECAPTCHA_PUBLIC_KEY={{ recaptcha_public_key }} -# RECAPTCHA_PRIVATE_KEY={{ recaptcha_private_key }} -{% endif %} - ################################### # Advanced settings ################################### -{% if password_scheme %} -# Specific password storage scheme -PASSWORD_SCHEME={{ password_scheme }} -{% endif %} +# 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={{ log_driver or 'json-file' }} + +# Docker-compose project name, this will prepended to containers names. +COMPOSE_PROJECT_NAME={{ compose_project_name or 'mailu' }} + +# Default password scheme used for newly created accounts and changed passwords +# (value: BLF-CRYPT, SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT) +PASSWORD_SCHEME={{ password_scheme or 'BLF-CRYPT' }} # Header to take the real ip from REAL_IP_HEADER={{ real_ip_header }} # IPs for nginx set_real_ip_from (CIDR list separated by commas) REAL_IP_FROM={{ real_ip_from }} + +# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no) +REJECT_UNLISTED_RECIPIENT={{ reject_unlisted_recipient }} diff --git a/setup/flavors/compose/setup.html b/setup/flavors/compose/setup.html index e4506e6d..0379ba82 100644 --- a/setup/flavors/compose/setup.html +++ b/setup/flavors/compose/setup.html @@ -4,15 +4,15 @@

Docker Compose expects a project file, named docker-compose.yml in a project directory. First create your project directory.

-
mkdir /mailu
+
mkdir {{ root }}
 

Then download the project file. A side configuration file makes it easier to read and check the configuration variables generated by the wizard.

-
cd /mailu
-wget {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }}
-wget {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }}
+
cd {{ root }}
+curl {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }} > docker-compose.yml
+curl {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }} > mailu.env
 
{% endcall %} @@ -30,7 +30,22 @@ files before going any further.

To start your compose project, simply run the Docker Compose up command.

-
cd /mailu
+
cd {{ root }}
 docker-compose up -d
 
+ +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: + +
docker-compose exec admin python manage.py admin {{ postmaster }} {{ domain }} PASSWORD
+
+ +

Login to the admin interface to change the password for a safe one, at +{% if admin_enabled %} +one of the hostnames +{{ hostnames.split(',')[0] }}{{ admin_path }}. +{% else %} +http://127.0.0.1:8080 (only directly from the host running docker). +{% endif %} +And choose the "Update password" option in the left menu. +

{% endcall %} diff --git a/setup/flavors/stack/docker-compose.yml b/setup/flavors/stack/docker-compose.yml new file mode 100644 index 00000000..f27b661f --- /dev/null +++ b/setup/flavors/stack/docker-compose.yml @@ -0,0 +1,128 @@ +{% set env='mailu.env' %} +# This file is auto-generated by the Mailu configuration wizard. +# Please read the documentation before attempting any change. +# Generated for {{ flavor }} flavor + +version: '3.6' + +services: + +# External dependencies + redis: + image: redis:alpine + restart: always + volumes: + - "{{ root }}/redis:/data" + +# Core services + front: + image: mailu/nginx:{{ version }} + env_file: {{ env }} + ports: + {% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %} + - target: {{ port }} + published: {{ port }} + mode: overlay + {% endfor %} + volumes: + - "{{ root }}/certs:/certs" + deploy: + replicas: 1 + + admin: + image: mailu/admin:{{ version }} + env_file: {{ env }} + {% if not admin_enabled %} + ports: + - 127.0.0.1:8080:80 + {% endif %} + volumes: + - "{{ root }}/data:/data" + - "{{ root }}/dkim:/dkim" + deploy: + replicas: 1 + + imap: + image: mailu/dovecot:{{ version }} + env_file: {{ env }} + environment: + # Default to 10.0.1.0/24 + - POD_ADDRESS_RANGE={{ subnet }} + volumes: + - "{{ root }}/mail:/mail" + - "{{ root }}/overrides:/overrides" + deploy: + replicas: 1 + + smtp: + image: mailu/postfix:{{ version }} + env_file: {{ env }} + environment: + - POD_ADDRESS_RANGE={{ subnet }} + volumes: + - "{{ root }}/overrides:/overrides" + deploy: + replicas: 1 + + # Optional services + {% if antispam_enabled %} + antispam: + image: mailu/rspamd:{{ version }} + env_file: {{ env }} + environment: + - POD_ADDRESS_RANGE={{ subnet }} + volumes: + - "{{ root }}/filter:/var/lib/rspamd" + - "{{ root }}/dkim:/dkim" + - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d" + deploy: + replicas: 1 + {% endif %} + + {% if antivirus_enabled %} + antivirus: + image: mailu/clamav:{{ version }} + env_file: {{ env }} + volumes: + - "{{ root }}/filter:/data" + deploy: + replicas: 1 + {% endif %} + + {% if webdav_enabled %} + webdav: + image: mailu/none:{{ version }} + env_file: {{ env }} + volumes: + - "{{ root }}/dav:/data" + deploy: + replicas: 1 + {% endif %} + + {% if fetchmail_enabled %} + fetchmail: + image: mailu/fetchmail:{{ version }} + env_file: {{ env }} + volumes: + - "{{ root }}/data:/data" + deploy: + replicas: 1 + {% endif %} + + {% if webmail_type != 'none' %} + webmail: + image: mailu/roundcube:{{ version }} + env_file: {{ env }} + volumes: + - "{{ root }}/webmail:/data" + deploy: + replicas: 1 + {% endif %} + +networks: + default: + driver: overlay + ipam: + driver: default + config: + - subnet: {{ subnet }} diff --git a/setup/flavors/stack/mailu.env b/setup/flavors/stack/mailu.env new file mode 120000 index 00000000..7123102b --- /dev/null +++ b/setup/flavors/stack/mailu.env @@ -0,0 +1 @@ +../compose/mailu.env \ No newline at end of file diff --git a/setup/flavors/stack/setup.html b/setup/flavors/stack/setup.html new file mode 100644 index 00000000..d68a6422 --- /dev/null +++ b/setup/flavors/stack/setup.html @@ -0,0 +1,60 @@ +{% import "macros.html" as macros %} + +{% call macros.panel("info", "Step 1 - Download your configuration files") %} +

Docker Stack expects a project file, named docker-compose.yml +in a project directory. First create your project directory.

+ +
mkdir -p /{{ root }}/{redis,certs,data,dkim,mail,overrides/rspamd,filter,dav,webmail}
+
+ +

Then download the project file. A side configuration file makes it easier +to read and check the configuration variables generated by the wizard.

+ +
cd {{ root }}
+curl {{ url_for('.file', uid=uid, filepath='docker-compose.yml', _external=True) }} > docker-compose.yml
+curl {{ url_for('.file', uid=uid, filepath='mailu.env', _external=True) }} > mailu.env
+
+{% endcall %} + + +{% call macros.panel("info", "Step 2 - Review the configuration") %} +

We did not insert any malicious code on purpose in the configurations we +distribute, but your download could have been intercepted, or our wizard +website could have been compromised, so make sure you check the configuration +files before going any further.

+ +

When you are done checking them, check them one last time.

+{% endcall %} + +{% call macros.panel("info", "Step 3 - Deploy docker stack") %} +

To deploy the docker stack use the following commands. For more information about setting up docker swarm nodes read the + docker documentation

+ +
cd {{ root }}
+docker swarm init
+docker stack deploy -c docker-compose.yml mailu
+
+ +In the docker stack deploy command, mailu is the app name. Feel free to change it.
+In order to display the running container you can use
+
docker ps
+or +
docker stack ps --no-trunc mailu
+Command for removing docker stack is +
docker stack rm mailu
+ +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: + +
docker exec $(docker ps | grep admin | cut -d ' ' -f1) python manage.py admin {{ postmaster }} {{ domain }} PASSWORD 
+
+ +

Login to the admin interface to change the password for a safe one, at +{% if admin_enabled %} +one of the hostnames +{{ hostnames.split(',')[0] }}{{ admin_path }}. +{% else %} +http://127.0.0.1:8080 (only directly from the host running docker). +{% endif %} +And choose the "Update password" option in the left menu. +

+{% endcall %} diff --git a/setup/server.py b/setup/server.py index 108f5043..bfe5ef15 100644 --- a/setup/server.py +++ b/setup/server.py @@ -32,9 +32,11 @@ def secret(length=16): def build_app(path): + #Hardcoded master as the only version for test purposes versions = [ - version for version in os.listdir(path) - if os.path.isdir(os.path.join(path, version)) + # version for version in os.listdir(path) + # if os.path.isdir(os.path.join(path, version)) + "master" ] app.jinja_env.trim_blocks = True @@ -63,6 +65,12 @@ def build_app(path): def wizard(): return flask.render_template('wizard.html') + @bp.route("/submit_flavor", methods=["POST"]) + def submit_flavor(): + data = flask.request.form.copy() + steps = sorted(os.listdir(path + "/" + version + "/templates/steps/" + data["flavor"])) + return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps) + @bp.route("/submit", methods=["POST"]) def submit(): data = flask.request.form.copy() diff --git a/setup/templates/base.html b/setup/templates/base.html index d40a4880..5be0b1eb 100644 --- a/setup/templates/base.html +++ b/setup/templates/base.html @@ -8,7 +8,7 @@

Mailu configuration

Version - {% for available in versions %} {% endfor %} diff --git a/setup/templates/macros.html b/setup/templates/macros.html index 579800d2..4af20c4e 100644 --- a/setup/templates/macros.html +++ b/setup/templates/macros.html @@ -9,10 +9,10 @@ {% endmacro %} -{% macro radio(name, value, emph, text) %} +{% macro radio(name, value, emph, text, current) %}