From 9f6848110a866f49df9dd8e89986c2b747e61a60 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 9 Mar 2022 13:10:24 +0100 Subject: [PATCH 01/10] Make gunicorn listen on ipv6 --- core/admin/start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/start.py b/core/admin/start.py index 0d7f2110..c5b3bc23 100755 --- a/core/admin/start.py +++ b/core/admin/start.py @@ -55,7 +55,7 @@ test_DNS() start_command=" ".join([ "gunicorn", f"--threads {str(os.cpu_count())}", - "-b :80", + "-b [::]:80", "--logger-class mailu.Logger", "--worker-tmp-dir /dev/shm", "--access-logfile -" if (log.root.level<=log.INFO) else "", From 35331a4295d1740c71e77debfec6c3081bc767c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Sch=C3=A4pers?= Date: Sun, 20 Mar 2022 13:27:39 +0100 Subject: [PATCH 02/10] Make gunicorn IPv6 conditional Only listen on [::]:80 in case SUBNET6 is defined, otherwise do the normal :80 --- core/admin/start.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/admin/start.py b/core/admin/start.py index c5b3bc23..debb4ec7 100755 --- a/core/admin/start.py +++ b/core/admin/start.py @@ -52,10 +52,15 @@ def test_DNS(): test_DNS() +bind_addr = ":80" +if os.environ.get("SUBNET6") is not None: + # If SUBNET6 is defined, gunicorn must listen on IPv6 as well as IPv4 + bind_addr = "[::]:80" + start_command=" ".join([ "gunicorn", f"--threads {str(os.cpu_count())}", - "-b [::]:80", + "-b", bind_addr, "--logger-class mailu.Logger", "--worker-tmp-dir /dev/shm", "--access-logfile -" if (log.root.level<=log.INFO) else "", From 8b1eb020e2be612361f33b8cf1dffe54c4abe238 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 14:05:42 +0100 Subject: [PATCH 03/10] Put IPv6 address in brackets --- setup/flavors/compose/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index 87b4183b..3ca8236a 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -33,7 +33,7 @@ services: - "{{ bind4 }}:{{ port }}:{{ port }}" {% endif %} {% if ipv6_enabled and bind6 %} - - "{{ bind6 }}:{{ port }}:{{ port }}" + - "[{{ bind6 }}]:{{ port }}:{{ port }}" {% endif %} {% endfor %} networks: From 7cc5d1f756504761a4cb3d7a1c7191dc3373f6c5 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 9 Mar 2022 13:24:59 +0100 Subject: [PATCH 04/10] Update documentation to reflect ip6tables support being experimental --- docs/faq.rst | 51 ++++++++++++--------------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 3f16fc6d..b6849eaa 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -145,51 +145,24 @@ Your mail service will be reachable for IMAP, POP3, SMTP and Webmail at the addr 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: +Docker IPv6 interfacing with ``ip6tables``, which is required for proper IPv6 support, is currently considered experimental. - 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: +You can enable experimental IPv6 support in docker via a custom ``/etc/docker/daemon.json`` file like this one: - - 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: +.. code-block:: json - - 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 `_). + { + "ipv6": true, + "experimental": true, + "fixed-cidr-v6": "fd00:1234:abcd::/48", + "ip6tables": true + } - -- `Robbert Klarenbeek `_ (docker-ipv6nat author) +and enabling the IPv6 checkbox in the `setup utility`_. -Okay, but I still want to use IPv6! Can I just use the installers IPv6 checkbox? **NO, YOU SHOULD NOT DO THAT!** Why you ask? -Mailu has its own trusted IPv4 network, every container inside this network can use e.g. the SMTP container without further -authentication. If you enabled IPv6 inside the setup assistant (and fixed the ports to also be exposed on IPv6) Docker will -still rewrite any incoming IPv6 requests to an IPv4 address, *which is located inside the trusted network*. Therefore any -incoming connection to the SMTP container will bypass the authentication stage by the front container regardless of your -settings and causes an Open Relay. And you really don't want this! +This setup however is not officially supported, and might result in unforeseen issues. +With bad misconfiguration you might even cause your instance to become an Open Relay, you have been warned! -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? From 1ad1d8d95d7dc77b729dc9a1e329d3b1cd6fb7c1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 15:15:24 +0100 Subject: [PATCH 05/10] Rewrite generation of gunicorn cmdline --- core/admin/start.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/core/admin/start.py b/core/admin/start.py index debb4ec7..f92d845e 100755 --- a/core/admin/start.py +++ b/core/admin/start.py @@ -52,20 +52,21 @@ def test_DNS(): test_DNS() -bind_addr = ":80" -if os.environ.get("SUBNET6") is not None: - # If SUBNET6 is defined, gunicorn must listen on IPv6 as well as IPv4 - bind_addr = "[::]:80" - -start_command=" ".join([ - "gunicorn", - f"--threads {str(os.cpu_count())}", - "-b", bind_addr, +cmdline = [ + "gunicorn", + "--threads", f"{os.cpu_count()}", + # If SUBNET6 is defined, gunicorn must listen on IPv6 as well as IPv4 + "-b", f"{'[::]' if os.environ.get('SUBNET6') else ''}:80", "--logger-class mailu.Logger", "--worker-tmp-dir /dev/shm", - "--access-logfile -" if (log.root.level<=log.INFO) else "", - "--error-logfile -", - "--preload", - "'mailu:create_app()'"]) + "--error-logfile", "-", + "--preload" +] -os.system(start_command) +# logging +if log.root.level <= log.INFO: + cmdline.extend(["--access-logfile", "-"]) + +cmdline.append("'mailu:create_app()'") + +os.system(" ".join(cmdline)) From 842be9b7c35dc55e5c21bedeec8ad4134198d70b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 15:55:35 +0100 Subject: [PATCH 06/10] Skip listen to v6 when SUBNET6 is not set --- core/nginx/conf/nginx.conf | 20 ++++++++++++++++++++ core/postfix/conf/main.cf | 4 ++-- webmails/nginx-webmail.conf | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index db161862..38cf7871 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -59,7 +59,9 @@ http { server { # Listen over HTTP listen 80; +{% if SUBNET6 %} listen [::]:80; +{% endif %} {% if TLS_FLAVOR == 'letsencrypt' %} location ^~ /.well-known/acme-challenge/ { proxy_pass http://127.0.0.1:8008; @@ -91,13 +93,17 @@ http { # Listen on HTTP only in kubernetes or behind reverse proxy {% if KUBERNETES_INGRESS == 'true' or TLS_FLAVOR in [ 'mail-letsencrypt', 'notls', 'mail' ] %} listen 80; +{% if SUBNET6 %} listen [::]:80; +{% endif %} {% endif %} # Only enable HTTPS if TLS is enabled with no error and not on kubernetes {% if KUBERNETES_INGRESS != 'true' and TLS and not TLS_ERROR %} listen 443 ssl http2; +{% if SUBNET6 %} listen [::]:443 ssl http2; +{% endif %} include /etc/nginx/tls.conf; ssl_stapling on; @@ -341,7 +347,9 @@ mail { # SMTP is always enabled, to avoid losing emails when TLS is failing server { listen 25; +{% if SUBNET6 %} listen [::]:25; +{% endif %} {% if TLS and not TLS_ERROR %} {% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %} ssl_certificate /certs/letsencrypt/live/mailu/fullchain.pem; @@ -363,7 +371,9 @@ mail { {% if not TLS_ERROR %} server { listen 143; +{% if SUBNET6 %} listen [::]:143; +{% endif %} {% if TLS %} starttls only; {% endif %} @@ -376,7 +386,9 @@ mail { server { listen 110; +{% if SUBNET6 %} listen [::]:110; +{% endif %} {% if TLS %} starttls only; {% endif %} @@ -389,7 +401,9 @@ mail { server { listen 587; +{% if SUBNET6 %} listen [::]:587; +{% endif %} {% if TLS %} starttls only; {% endif %} @@ -401,7 +415,9 @@ mail { {% if TLS %} server { listen 465 ssl; +{% if SUBNET6 %} listen [::]:465 ssl; +{% endif %} protocol smtp; smtp_auth plain login; auth_http_header Auth-Port 465; @@ -409,7 +425,9 @@ mail { server { listen 993 ssl; +{% if SUBNET6 %} listen [::]:993 ssl; +{% endif %} protocol imap; imap_auth plain; auth_http_header Auth-Port 993; @@ -419,7 +437,9 @@ mail { server { listen 995 ssl; +{% if SUBNET6 %} listen [::]:995 ssl; +{% endif %} protocol pop3; pop3_auth plain; auth_http_header Auth-Port 995; diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 0f6fd392..2e038f61 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -14,7 +14,7 @@ queue_directory = /queue message_size_limit = {{ MESSAGE_SIZE_LIMIT }} # Relayed networks -mynetworks = 127.0.0.1/32 [::1]/128 {{ SUBNET }} {% if SUBNET6 %}{{ "[{}]/{}".format(*SUBNET6.split("/")) }}{% endif %} {% if RELAYNETS %}{{ RELAYNETS.split(",") | join(" ") }}{% endif %} +mynetworks = 127.0.0.1/32 {{ SUBNET }} {% if SUBNET6 %}[::1]/128 {{ "[{}]/{}".format(*SUBNET6.translate({91: None, 93: None}).split("/")) }}{% endif %} {% if RELAYNETS %}{{ RELAYNETS.split(",") | join(" ") }}{% endif %} # Empty alias list to override the configuration variable and disable NIS alias_maps = @@ -121,7 +121,7 @@ smtpd_relay_restrictions = unverified_recipient_reject_reason = Address lookup failure -smtpd_authorized_xclient_hosts={{ SUBNET }}{% if SUBNET6 %},[{{ SUBNET6 }}]{% endif %} +smtpd_authorized_xclient_hosts={{ SUBNET }}{% if SUBNET6 %},{{ "[{}]/{}".format(*SUBNET6.translate({91: None, 93: None}).split("/")) }}{% endif %} ############### # Milter diff --git a/webmails/nginx-webmail.conf b/webmails/nginx-webmail.conf index fde7adfd..8772c8c8 100644 --- a/webmails/nginx-webmail.conf +++ b/webmails/nginx-webmail.conf @@ -1,6 +1,8 @@ server { listen 80 default_server; +{% if SUBNET6 %} listen [::]:80 default_server; +{% endif %} resolver {{ RESOLVER }} valid=30s; {% if WEBMAIL == 'roundcube' %} From 120a7e83681667a8d1c8599d1c88227ce7f73e9f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 16:18:09 +0100 Subject: [PATCH 07/10] Still prefer docker-ipv6nat --- docs/faq.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index b6849eaa..c73304b0 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -147,11 +147,30 @@ How to make IPv6 work? Docker IPv6 interfacing with ``ip6tables``, which is required for proper IPv6 support, is currently considered experimental. -You can enable experimental IPv6 support in docker via a custom ``/etc/docker/daemon.json`` file like this one: +Although the supposed way to enable IPv6 would be to give each container a publicly routable address, docker's IPv6 support +uses NAT to pass outside connections to the containers. + +Currently we recommend to use `docker-ipv6nat` by `Robert Klarenbeek ` instead of docker's +experimental support. + +Before enabling IPv6 you **MUST** disable the userland-proxy in your ``/etc/docker/daemon.json`` to not create an Open Relay! .. code-block:: json { + "userland-proxy": false + } + +You can enable `docker-ipv6nat` like this: + + docker run -d --name ipv6nat --privileged --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v /lib/modules:/lib/modules:ro robbertkl/ipv6nat + +If you want to try docker's experimental IPv6 support, it can be enabled like this: + +.. code-block:: json + + { + "userland-proxy": false, "ipv6": true, "experimental": true, "fixed-cidr-v6": "fd00:1234:abcd::/48", From 25635396e7ba024898e838afbf32bf3e07b31191 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 16:34:32 +0100 Subject: [PATCH 08/10] Bind webdav to port only --- optional/radicale/radicale.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional/radicale/radicale.conf b/optional/radicale/radicale.conf index 6c99d8e0..fb1304d1 100644 --- a/optional/radicale/radicale.conf +++ b/optional/radicale/radicale.conf @@ -1,5 +1,5 @@ [server] -hosts = 0.0.0.0:5232, [::]:5232 +hosts = :5232 ssl = False [encoding] From 84d156d02f9b7641de6a312538e96667de900f1f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Jan 2023 16:53:36 +0100 Subject: [PATCH 09/10] Add towncrier file --- towncrier/2630.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/2630.feature diff --git a/towncrier/2630.feature b/towncrier/2630.feature new file mode 100644 index 00000000..29865199 --- /dev/null +++ b/towncrier/2630.feature @@ -0,0 +1 @@ +Improved IPv6 support From 21ac230cce57f4fd92ca108967acab3cf4872118 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 28 Jan 2023 16:32:21 +0100 Subject: [PATCH 10/10] Make olefy.py listen on all interfaces --- core/oletools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/oletools/Dockerfile b/core/oletools/Dockerfile index 8526e506..39fd0e18 100644 --- a/core/oletools/Dockerfile +++ b/core/oletools/Dockerfile @@ -19,7 +19,7 @@ HEALTHCHECK --start-period=60s CMD echo PING|nc -q1 127.0.0.1 11343|grep "PONG" USER nobody:nobody ENV \ - OLEFY_BINDADDRESS="0.0.0.0" \ + OLEFY_BINDADDRESS="" \ OLEFY_BINDPORT="11343" \ OLEFY_OLEVBA_PATH="/app/venv/bin/olevba" \ OLEFY_PYTHON_PATH="/app/venv/bin/python3" \