diff --git a/services/unbound/Dockerfile b/services/unbound/Dockerfile
new file mode 100644
index 00000000..1b84855c
--- /dev/null
+++ b/services/unbound/Dockerfile
@@ -0,0 +1,18 @@
+FROM python:3-alpine
+
+RUN apk add --no-cache unbound curl bind-tools \
+ && pip3 install jinja2 \
+ && curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache \
+ && chown root:unbound /etc/unbound \
+ && chmod 775 /etc/unbound \
+ && apk del --no-cache curl \
+ && /usr/sbin/unbound-anchor -a /etc/unbound/trusted-key.key | true
+
+COPY start.py /start.py
+COPY unbound.conf /unbound.conf
+
+EXPOSE 53/udp 53/tcp
+
+CMD /start.py
+
+HEALTHCHECK CMD dig @127.0.0.1 || exit 1
diff --git a/services/unbound/start.py b/services/unbound/start.py
new file mode 100755
index 00000000..82e017f7
--- /dev/null
+++ b/services/unbound/start.py
@@ -0,0 +1,9 @@
+#!/usr/local/bin/python3
+
+import jinja2
+import os
+
+convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ))
+convert("/unbound.conf", "/etc/unbound/unbound.conf")
+
+os.execv("/usr/sbin/unbound", ["-c /etc/unbound/unbound.conf"])
diff --git a/services/unbound/unbound.conf b/services/unbound/unbound.conf
new file mode 100644
index 00000000..d54cbfbc
--- /dev/null
+++ b/services/unbound/unbound.conf
@@ -0,0 +1,19 @@
+server:
+ verbosity: 1
+ interface: 0.0.0.0
+ interface: ::0
+ logfile: /dev/stdout
+ do-ip4: yes
+ do-ip6: yes
+ do-udp: yes
+ do-tcp: yes
+ do-daemonize: no
+ access-control: {{ SUBNET }} allow
+ directory: "/etc/unbound"
+ username: root
+ auto-trust-anchor-file: trusted-key.key
+ root-hints: "/etc/unbound/root.hints"
+ hide-identity: yes
+ hide-version: yes
+ max-udp-size: 4096
+ msg-buffer-size: 65552
diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml
index b01bb8fd..81b6bcb2 100644
--- a/setup/flavors/compose/docker-compose.yml
+++ b/setup/flavors/compose/docker-compose.yml
@@ -28,6 +28,16 @@ services:
{% endfor %}
volumes:
- "{{ root }}/certs:/certs"
+
+ {% if resolver_enabled %}
+ resolver:
+ image: mailu/unbound:{{ version }}
+ env_file: {{ env }}
+ restart: always
+ networks:
+ default:
+ ipv4_address: {{ dns }}
+ {% endif %}
admin:
image: mailu/admin:{{ version }}
@@ -58,6 +68,11 @@ services:
- "{{ root }}/overrides:/overrides"
depends_on:
- front
+ {% if resolver_enabled %}
+ - resolver
+ dns:
+ - {{ dns }}
+ {% endif %}
# Optional services
{% if antispam_enabled %}
@@ -70,6 +85,11 @@ services:
- "{{ root }}/overrides/rspamd:/etc/rspamd/override.d"
depends_on:
- front
+ {% if resolver_enabled %}
+ - resolver
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
{% if antivirus_enabled %}
@@ -78,6 +98,12 @@ services:
env_file: {{ env }}
volumes:
- "{{ root }}/filter:/data"
+ {% if resolver_enabled %}
+ depends_on:
+ - resolver
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
{% if webdav_enabled %}
@@ -92,6 +118,12 @@ services:
fetchmail:
image: mailu/fetchmail:{{ version }}
env_file: {{ env }}
+ {% if resolver_enabled %}
+ depends_on:
+ - resolver
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
# Webmail
@@ -104,3 +136,13 @@ services:
depends_on:
- imap
{% endif %}
+
+{% if resolver_enabled %}
+networks:
+ default:
+ driver: bridge
+ ipam:
+ driver: default
+ config:
+ - subnet: {{ subnet }}
+{% endif %}
diff --git a/setup/flavors/compose/mailu.env b/setup/flavors/compose/mailu.env
index 9fc1197d..4a14de63 100644
--- a/setup/flavors/compose/mailu.env
+++ b/setup/flavors/compose/mailu.env
@@ -25,6 +25,9 @@ SECRET_KEY={{ secret(16) }}
# PUBLIC_IPV4= {{ bind4 }} (default: 127.0.0.1)
# PUBLIC_IPV6= {{ bind6 }} (default: ::1)
+# Subnet
+SUBNET={{ subnet }}
+
# Main mail domain
DOMAIN={{ domain }}
diff --git a/setup/flavors/stack/docker-compose.yml b/setup/flavors/stack/docker-compose.yml
index f27b661f..b9537e94 100644
--- a/setup/flavors/stack/docker-compose.yml
+++ b/setup/flavors/stack/docker-compose.yml
@@ -28,6 +28,15 @@ services:
- "{{ root }}/certs:/certs"
deploy:
replicas: 1
+
+ {% if resolver_enabled %}
+ resolver:
+ image: mailu/unbound:{{ version }}
+ env_file: {{ env }}
+ networks:
+ default:
+ ipv4_address: {{ dns }}
+ {% endif %}
admin:
image: mailu/admin:{{ version }}
@@ -63,6 +72,10 @@ services:
- "{{ root }}/overrides:/overrides"
deploy:
replicas: 1
+ {% if resolver_enabled %}
+ dns:
+ - {{ dns }}
+ {% endif %}
# Optional services
{% if antispam_enabled %}
@@ -77,6 +90,10 @@ services:
- "{{ root }}/overrides/rspamd:/etc/rspamd/override.d"
deploy:
replicas: 1
+ {% if resolver_enabled %}
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
{% if antivirus_enabled %}
@@ -87,6 +104,10 @@ services:
- "{{ root }}/filter:/data"
deploy:
replicas: 1
+ {% if resolver_enabled %}
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
{% if webdav_enabled %}
@@ -107,6 +128,10 @@ services:
- "{{ root }}/data:/data"
deploy:
replicas: 1
+ {% if resolver_enabled %}
+ dns:
+ - {{ dns }}
+ {% endif %}
{% endif %}
{% if webmail_type != 'none' %}
diff --git a/setup/server.py b/setup/server.py
index bfe5ef15..6f60c3c0 100644
--- a/setup/server.py
+++ b/setup/server.py
@@ -7,6 +7,7 @@ import jinja2
import uuid
import string
import random
+import ipaddress
app = flask.Flask(__name__)
@@ -75,6 +76,7 @@ def build_app(path):
def submit():
data = flask.request.form.copy()
data['uid'] = str(uuid.uuid4())
+ data['dns'] = str(ipaddress.IPv4Network(data['subnet'])[-2])
db.set(data['uid'], json.dumps(data))
return flask.redirect(flask.url_for('.setup', uid=data['uid']))
diff --git a/setup/templates/steps/compose/03_expose.html b/setup/templates/steps/compose/03_expose.html
index df121c7d..783c2037 100644
--- a/setup/templates/steps/compose/03_expose.html
+++ b/setup/templates/steps/compose/03_expose.html
@@ -26,6 +26,19 @@ avoid generic all-interfaces addresses like 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 0.0.0.0
or ::
+
MX
record. Hostnames must be coma-separated.
You server will be available under a main hostname but may expose multiple public diff --git a/tests/build.yml b/tests/build.yml index 5f360ece..ed5b75fe 100644 --- a/tests/build.yml +++ b/tests/build.yml @@ -6,6 +6,10 @@ services: image: ${DOCKER_ORG:-mailu}/nginx:${VERSION:-local} build: ../core/nginx + resolver: + image: ${DOCKER_ORG:-mailu}/unbound:${VERSION:-local} + build: ../services/unbound + imap: image: ${DOCKER_ORG:-mailu}/dovecot:${VERSION:-local} build: ../core/dovecot