2630: Improved IPv6 support #2 r=mergify[bot] a=ghostwheel42

## What type of PR?

enhancement, bug-fix, documentation

## What does this PR do?

This is based on #2272 and adds some more fixes.

### Related issue(s)
- closes #1789
- closes #2392


Co-authored-by: Chris <chris@niduroki.net>
Co-authored-by: Chris Schäpers <chris@niduroki.net>
Co-authored-by: Alexander Graf <ghostwheel42@users.noreply.github.com>
main
bors[bot] 2 years ago committed by GitHub
commit 9bd76536a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -52,15 +52,21 @@ def test_DNS():
test_DNS()
start_command=" ".join([
"gunicorn",
f"--threads {str(os.cpu_count())}",
"-b :80",
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))

@ -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;

@ -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" \

@ -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

@ -145,51 +145,43 @@ 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:
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.
- 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 <https://github.com/docker/docker/issues/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 <https://github.com/docker/docker/issues/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 <https://github.com/docker/docker/issues/14856>`_) and has various issues, like:
Currently we recommend to use `docker-ipv6nat` by `Robert Klarenbeek <https://github.com/robbertkl>` instead of docker's
experimental support.
- It can use a lot of RAM (`docker/docker#11185 <https://github.com/docker/docker/issues/11185>`_)
- Source IP addresses are rewritten, making it completely unusable for many purposes, e.g. mail servers
(`docker/docker#17666 <https://github.com/docker/docker/issues/17666>`_),
(`docker/libnetwork#1099 <https://github.com/docker/libnetwork/issues/1099>`_).
Before enabling IPv6 you **MUST** disable the userland-proxy in your ``/etc/docker/daemon.json`` to not create an Open Relay!
-- `Robbert Klarenbeek <https://github.com/robbertkl>`_ (docker-ipv6nat author)
.. code-block:: json
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!
{
"userland-proxy": false
}
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.
You can enable `docker-ipv6nat` like this:
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:
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
.. code-block:: bash
If you want to try docker's experimental IPv6 support, it can be enabled like this:
docker run -d --restart=always -v /var/run/docker.sock:/var/run/docker.sock:ro --privileged --net=host robbertkl/ipv6nat
.. code-block:: json
{
"userland-proxy": false,
"ipv6": true,
"experimental": true,
"fixed-cidr-v6": "fd00:1234:abcd::/48",
"ip6tables": true
}
and enabling the IPv6 checkbox in the `setup utility`_.
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!
.. _`docker-ipv6nat`: https://github.com/robbertkl/docker-ipv6nat
.. _`setup utility`: https://setup.mailu.io
How does Mailu scale up?

@ -1,5 +1,5 @@
[server]
hosts = 0.0.0.0:5232, [::]:5232
hosts = :5232
ssl = False
[encoding]

@ -33,7 +33,7 @@ services:
- "{{ bind4 }}:{{ port }}:{{ port }}"
{% endif %}
{% if ipv6_enabled and bind6 %}
- "{{ bind6 }}:{{ port }}:{{ port }}"
- "[{{ bind6 }}]:{{ port }}:{{ port }}"
{% endif %}
{% endfor %}
networks:

@ -0,0 +1 @@
Improved IPv6 support

@ -1,6 +1,8 @@
server {
listen 80 default_server;
{% if SUBNET6 %}
listen [::]:80 default_server;
{% endif %}
resolver {{ RESOLVER }} valid=30s;
{% if WEBMAIL == 'roundcube' %}

Loading…
Cancel
Save