From 14a30ee102ee05228341c2442f37c8f8e77fb89e Mon Sep 17 00:00:00 2001 From: Ionut Filip Date: Thu, 17 Jan 2019 16:24:52 +0200 Subject: [PATCH 1/8] Added IPv6 as optional --- setup/flavors/compose/docker-compose.yml | 8 ++++++- setup/flavors/compose/mailu.env | 3 +++ setup/server.py | 15 ++++++++++++- setup/static/render.js | 13 ++++++++++++ setup/templates/steps/compose/03_expose.html | 22 +++++++++++++------- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index 67408bee..a1d985e4 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -3,7 +3,7 @@ # Please read the documentation before attempting any change. # Generated for {{ flavor }} flavor -version: '3.6' +version: '2.2' services: @@ -160,8 +160,14 @@ services: networks: default: + {% if ipv6_enabled %} + enable_ipv6: true + {% endif %} driver: bridge ipam: driver: default config: - subnet: {{ subnet }} + {% if ipv6_enabled %} + - subnet: {{ subnet6 }} + {% endif %} diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env index 7d160011..341b7634 100644 --- a/setup/flavors/compose/mailu.env +++ b/setup/flavors/compose/mailu.env @@ -27,6 +27,9 @@ SECRET_KEY={{ secret(16) }} # Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!) SUBNET={{ subnet }} +{% if ipv6_enabled %} +SUBNET6={{ subnet6 }} +{% endif %} # Main mail domain DOMAIN={{ domain }} diff --git a/setup/server.py b/setup/server.py index fea27ead..134989f4 100644 --- a/setup/server.py +++ b/setup/server.py @@ -9,6 +9,7 @@ import string import random import ipaddress import hashlib +import time version = os.getenv("this_version") @@ -33,6 +34,17 @@ def secret(length=16): for _ in range(length) ) +#Original copied from https://github.com/andrewlkho/ulagen +def random_ipv6_subnet(): + eui64 = uuid.getnode() >> 24 << 48 | 0xfffe000000 | uuid.getnode() & 0xffffff + eui64_canon = "-".join([format(eui64, "02X")[i:i+2] for i in range(0, 18, 2)]) + + h = hashlib.sha1() + h.update((eui64_canon + str(time.time() - time.mktime((1900, 1, 1, 0, 0, 0, 0, 1, -1)))).encode('utf-8')) + globalid = h.hexdigest()[0:10] + + prefix = ":".join(("fd" + globalid[0:2], globalid[2:6], globalid[6:10])) + return prefix def build_app(path): @@ -69,8 +81,9 @@ def build_app(path): @root_bp.route("/submit_flavor", methods=["POST"]) def submit_flavor(): data = flask.request.form.copy() + subnet6 = random_ipv6_subnet() steps = sorted(os.listdir(os.path.join(path, "templates", "steps", data["flavor"]))) - return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps) + return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps, subnet6=subnet6) @prefix_bp.route("/submit", methods=["POST"]) @root_bp.route("/submit", methods=["POST"]) diff --git a/setup/static/render.js b/setup/static/render.js index 23afcbec..e501fffb 100644 --- a/setup/static/render.js +++ b/setup/static/render.js @@ -86,3 +86,16 @@ $(document).ready(function() { } }); }); + +$(document).ready(function() { + if ($('#enable_ipv6').prop('checked')) { + $("#ipv6").show(); + } + $("#enable_ipv6").change(function() { + if ($(this).is(":checked")) { + $("#ipv6").show(); + } else { + $("#ipv6").hide(); + } + }); +}); diff --git a/setup/templates/steps/compose/03_expose.html b/setup/templates/steps/compose/03_expose.html index 0c912778..5d7b29ad 100644 --- a/setup/templates/steps/compose/03_expose.html +++ b/setup/templates/steps/compose/03_expose.html @@ -17,13 +17,27 @@ avoid generic all-interfaces addresses like 0.0.0.0 or :: + + -
+
+ +
+ +
@@ -33,12 +47,6 @@ avoid generic all-interfaces addresses like 0.0.0.0 or ::
-
- - -
-

You server will be available under a main hostname but may expose multiple public hostnames. Every e-mail domain that points to this server must have one of the hostnames in its MX record. Hostnames must be coma-separated.

From 3ae1c75c551ba922de5df7503ed4d06867c934fc Mon Sep 17 00:00:00 2001 From: Ionut Filip Date: Thu, 17 Jan 2019 16:24:52 +0200 Subject: [PATCH 2/8] Added IPv6 as optional --- setup/flavors/compose/docker-compose.yml | 8 ++++++- setup/flavors/compose/mailu.env | 3 +++ setup/server.py | 15 ++++++++++++- setup/static/render.js | 13 ++++++++++++ setup/templates/steps/compose/03_expose.html | 22 +++++++++++++------- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index 67408bee..a1d985e4 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -3,7 +3,7 @@ # Please read the documentation before attempting any change. # Generated for {{ flavor }} flavor -version: '3.6' +version: '2.2' services: @@ -160,8 +160,14 @@ services: networks: default: + {% if ipv6_enabled %} + enable_ipv6: true + {% endif %} driver: bridge ipam: driver: default config: - subnet: {{ subnet }} + {% if ipv6_enabled %} + - subnet: {{ subnet6 }} + {% endif %} diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env index 7d160011..341b7634 100644 --- a/setup/flavors/compose/mailu.env +++ b/setup/flavors/compose/mailu.env @@ -27,6 +27,9 @@ SECRET_KEY={{ secret(16) }} # Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!) SUBNET={{ subnet }} +{% if ipv6_enabled %} +SUBNET6={{ subnet6 }} +{% endif %} # Main mail domain DOMAIN={{ domain }} diff --git a/setup/server.py b/setup/server.py index 556d4b3a..4dfd49ae 100644 --- a/setup/server.py +++ b/setup/server.py @@ -9,6 +9,7 @@ import string import random import ipaddress import hashlib +import time version = os.getenv("this_version", "master") @@ -33,6 +34,17 @@ def secret(length=16): for _ in range(length) ) +#Original copied from https://github.com/andrewlkho/ulagen +def random_ipv6_subnet(): + eui64 = uuid.getnode() >> 24 << 48 | 0xfffe000000 | uuid.getnode() & 0xffffff + eui64_canon = "-".join([format(eui64, "02X")[i:i+2] for i in range(0, 18, 2)]) + + h = hashlib.sha1() + h.update((eui64_canon + str(time.time() - time.mktime((1900, 1, 1, 0, 0, 0, 0, 1, -1)))).encode('utf-8')) + globalid = h.hexdigest()[0:10] + + prefix = ":".join(("fd" + globalid[0:2], globalid[2:6], globalid[6:10])) + return prefix def build_app(path): @@ -69,8 +81,9 @@ def build_app(path): @root_bp.route("/submit_flavor", methods=["POST"]) def submit_flavor(): data = flask.request.form.copy() + subnet6 = random_ipv6_subnet() steps = sorted(os.listdir(os.path.join(path, "templates", "steps", data["flavor"]))) - return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps) + return flask.render_template('wizard.html', flavor=data["flavor"], steps=steps, subnet6=subnet6) @prefix_bp.route("/submit", methods=["POST"]) @root_bp.route("/submit", methods=["POST"]) diff --git a/setup/static/render.js b/setup/static/render.js index 23afcbec..e501fffb 100644 --- a/setup/static/render.js +++ b/setup/static/render.js @@ -86,3 +86,16 @@ $(document).ready(function() { } }); }); + +$(document).ready(function() { + if ($('#enable_ipv6').prop('checked')) { + $("#ipv6").show(); + } + $("#enable_ipv6").change(function() { + if ($(this).is(":checked")) { + $("#ipv6").show(); + } else { + $("#ipv6").hide(); + } + }); +}); diff --git a/setup/templates/steps/compose/03_expose.html b/setup/templates/steps/compose/03_expose.html index 837b7bba..d54985d4 100644 --- a/setup/templates/steps/compose/03_expose.html +++ b/setup/templates/steps/compose/03_expose.html @@ -18,13 +18,27 @@ avoid generic all-interfaces addresses like 0.0.0.0 or :: + +
-
+
+ +
+ +

The unbound resolver enables Mailu to do DNSsec verification, DNS root lookups and caching. This also helps the antispam service not to get blocked by the public or ISP DNS servers.

@@ -34,12 +48,6 @@ avoid generic all-interfaces addresses like 0.0.0.0 or ::
-

-
- - -

You server will be available under a main hostname but may expose multiple public hostnames. Every e-mail domain that points to this server must have one of the From 7a9685bcb93a8a2e5ebd1b8598952be9534a62d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Thu, 17 Jan 2019 16:32:47 +0200 Subject: [PATCH 3/8] Resolve admin during start to work around Docker DNS flaky-ness --- core/dovecot/start.py | 8 +++++--- core/postfix/start.py | 14 ++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/dovecot/start.py b/core/dovecot/start.py index 15e370de..bae92260 100755 --- a/core/dovecot/start.py +++ b/core/dovecot/start.py @@ -16,10 +16,11 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) def start_podop(): os.setuid(8) + url = "http://" + os.environ["ADMIN_ADDRESS"] + "/internal/dovecot/§" run_server(0, "dovecot", "/tmp/podop.socket", [ - ("quota", "url", "http://admin/internal/dovecot/§"), - ("auth", "url", "http://admin/internal/dovecot/§"), - ("sieve", "url", "http://admin/internal/dovecot/§"), + ("quota", "url", url ), + ("auth", "url", url), + ("sieve", "url", url), ]) def convert(src, dst): @@ -42,6 +43,7 @@ def resolve(hostname): # Actual startup script os.environ["FRONT_ADDRESS"] = resolve(os.environ.get("FRONT_ADDRESS", "front")) os.environ["REDIS_ADDRESS"] = resolve(os.environ.get("REDIS_ADDRESS", "redis")) +os.environ["ADMIN_ADDRESS"] = resolve(os.environ.get("ADMIN_ADDRESS", "admin")) if os.environ["WEBMAIL"] != "none": os.environ["WEBMAIL_ADDRESS"] = resolve(os.environ.get("WEBMAIL_ADDRESS", "webmail")) diff --git a/core/postfix/start.py b/core/postfix/start.py index a06b3833..e3b3eb40 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -17,14 +17,15 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) def start_podop(): os.setuid(100) + url = "http://" + os.environ["ADMIN_ADDRESS"] + "/internal/postfix/" # TODO: Remove verbosity setting from Podop? run_server(0, "postfix", "/tmp/podop.socket", [ - ("transport", "url", "http://admin/internal/postfix/transport/§"), - ("alias", "url", "http://admin/internal/postfix/alias/§"), - ("domain", "url", "http://admin/internal/postfix/domain/§"), - ("mailbox", "url", "http://admin/internal/postfix/mailbox/§"), - ("senderaccess", "url", "http://admin/internal/postfix/sender/access/§"), - ("senderlogin", "url", "http://admin/internal/postfix/sender/login/§") + ("transport", "url", url + "transport/§"), + ("alias", "url", url + "alias/§"), + ("domain", "url", url + "domain/§"), + ("mailbox", "url", url + "mailbox/§"), + ("senderaccess", "url", url + "sender/access/§"), + ("senderlogin", "url", url + "sender/login/§") ]) def convert(src, dst): @@ -46,6 +47,7 @@ def resolve(hostname): # Actual startup script os.environ["FRONT_ADDRESS"] = resolve(os.environ.get("FRONT_ADDRESS", "front")) +os.environ["ADMIN_ADDRESS"] = resolve(os.environ.get("ADMIN_ADDRESS", "admin")) os.environ["HOST_ANTISPAM"] = os.environ.get("HOST_ANTISPAM", "antispam:11332") os.environ["HOST_LMTP"] = os.environ.get("HOST_LMTP", "imap:2525") From 34608727471a24946378a48856118c8567fee45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Thu, 17 Jan 2019 17:56:00 +0200 Subject: [PATCH 4/8] Documentation on IPv6 --- CHANGELOG.md | 1 + docs/faq.rst | 43 ++++++++++++++++++++ setup/templates/steps/compose/03_expose.html | 3 +- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23cbebb0..cf73a463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ v1.6.0 - unreleased - Enhancement: Include favicon package ([#801](https://github.com/Mailu/Mailu/issues/801), ([#802](https://github.com/Mailu/Mailu/issues/802)) - Enhancement: Add logging at critical places in python start.py scripts. Implement LOG_LEVEL to control verbosity ([#588](https://github.com/Mailu/Mailu/issues/588)) - Enhancement: Mark message as seen when reporting as spam +- Enhancement: Better support and document IPv6 ([#827](https://github.com/Mailu/Mailu/issues/827)) - Upstream: Update Roundcube - Upstream: Update Rainloop - Bug: Rainloop fails with "domain not allowed" ([#93](https://github.com/Mailu/Mailu/issues/93)) diff --git a/docs/faq.rst b/docs/faq.rst index 2669d9d1..45f5534b 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -134,6 +134,49 @@ You're mail service will be reachable for IMAP, POP3, SMTP and Webmail at the ad *Issue reference:* `742`_, `747`_. +How to make IPv6 work? +`````````````````````` + +Docker currently does not expose the IPv6 ports properly, as it does not interface with ``ip6tables``. +Lets start with quoting everything that's wrong: + + Unfortunately, initially Docker was not created with IPv6 in mind. + It was added later and, while it has come a long way, is still not as usable as one would want. + Much discussion is still going on as to how IPv6 should be used in a containerized world; + See the various GitHub issues linked below: + + - Giving each container a publicly routable address means all ports (even unexposed / unpublished ports) are suddenly + reachable by everyone, if no additional filtering is done + (`docker/docker#21614 `_) + - By default, each container gets a random IPv6, making it impossible to do properly do DNS; + the alternative is to assign a specific IPv6 address to each container, + still an administrative hassle (`docker/docker#13481 `_) + - Published ports won't work on IPv6, unless you have the userland proxy enabled + (which, for now, is enabled by default in Docker) + - The userland proxy, however, seems to be on its way out + (`docker/docker#14856 `_) and has various issues, like: + + - It can use a lot of RAM (`docker/docker#11185 `_) + - Source IP addresses are rewritten, making it completely unusable for many purposes, e.g. mail servers + (`docker/docker#17666 `_), + (`docker/libnetwork#1099 `_). + + -- `Robbert Klarenbeek `_ (docker-ipv6nat author) + +So, how to make it work? Well, by using `docker-ipv6nat`_! This nifty container will set up ``ip6tables``, +just as Docker would do for IPv4. We know that nat-ing is not advised in IPv6, +however exposing all containers to public network neither. The choice is ultimately yous. + +Mailu `setup utility`_ generates a safe IPv6 ULA subnet by default. So when you run the following command, +Mailu will start to function on IPv6: + +.. code-block:: bash + + docker run -d --restart=always -v /var/run/docker.sock:/var/run/docker.sock:ro --privileged --net=host robbertkl/ipv6nat + +.. _`docker-ipv6nat`: https://github.com/robbertkl/docker-ipv6nat +.. _`setup utility`: https://setup.mailu.io + How does Mailu scale up? ```````````````````````` diff --git a/setup/templates/steps/compose/03_expose.html b/setup/templates/steps/compose/03_expose.html index d54985d4..c909fc9b 100644 --- a/setup/templates/steps/compose/03_expose.html +++ b/setup/templates/steps/compose/03_expose.html @@ -31,8 +31,7 @@ avoid generic all-interfaces addresses like 0.0.0.0 or ::