From c2ec97d641883747fd3a45a4d130b8068b19dc3a Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Thu, 9 Apr 2026 15:16:18 +0200 Subject: [PATCH] mail: listen on the bridge IP when requiresValidCertificate is set, we ended up injecting mutliple IP addresses for my.domain.com - 172.18.0.1 (bridge) and the mail container IP. Since the mail server is not running on the bridge, email may or may not be sent depending on which IP is picked up by the app. The solution is to make the mail container listen on the bridge as well. The other solution might have been to introduce a new subdomain for mail container and ensuring it is different from the dashboard subdomain. That way we can route the requests to different IPs. --- CHANGES | 2 +- src/docker.js | 12 ++++++++---- src/infra_version.js | 2 +- src/mailserver.js | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index a04584085..858aeabff 100644 --- a/CHANGES +++ b/CHANGES @@ -3223,4 +3223,4 @@ * apppasswords: generate easier to type passwords * logs: escape and unescape new lines * backups/volumes: rename 'mountpoint' to 'User-managed Mount Point' - +* mail: listen on the bridge IP diff --git a/src/docker.js b/src/docker.js index 7221189b7..e012c6450 100644 --- a/src/docker.js +++ b/src/docker.js @@ -716,11 +716,15 @@ async function createSubcontainer(app, name, cmd, options) { containerOptions.HostConfig.NetworkMode = 'cloudron'; // user defined bridge network // Do not inject for AdGuard. It ends up resolving the dashboard domain as the docker bridge IP - if (manifest.id !== 'com.adguard.home.cloudronapp') containerOptions.HostConfig.ExtraHosts.push(`${dashboardFqdn}:172.18.0.1`); + if (manifest.id !== 'com.adguard.home.cloudronapp') { + containerOptions.HostConfig.ExtraHosts.push(`${dashboardFqdn}:${constants.DOCKER_IPv4_GATEWAY}`); - if (manifest.addons?.sendmail?.requiresValidCertificate) { - const { fqdn:mailFqdn } = await mailServer.getLocation(); - containerOptions.HostConfig.ExtraHosts.push(`${mailFqdn}:${constants.MAIL_SERVICE_IPv4}`); + if (manifest.addons?.sendmail?.requiresValidCertificate) { + const { fqdn:mailFqdn } = await mailServer.getLocation(); + // When mail fqdn and dashboard fqdn are the same, they must resolve to the same IP (currently bridge IP) + // if they are not the same, the requests can go to any of the multiple IPs + if (dashboardFqdn !== mailFqdn) containerOptions.HostConfig.ExtraHosts.push(`${mailFqdn}:${constants.DOCKER_IPv4_GATEWAY}`); + } } containerOptions.NetworkingConfig = { diff --git a/src/infra_version.js b/src/infra_version.js index 60f14388b..fd23c4a46 100644 --- a/src/infra_version.js +++ b/src/infra_version.js @@ -4,7 +4,7 @@ export default { // a version change recreates all containers with latest docker config - 'version': '49.9.0', + 'version': '49.10.0', // a major version bump in the db containers will trigger the restore logic that uses the db dumps // docker inspect --format='{{index .RepoDigests 0}}' $IMAGE to get the sha256 . note this has registry in it because manifest id is registry specific! diff --git a/src/mailserver.js b/src/mailserver.js index 041902af1..c0d72df02 100644 --- a/src/mailserver.js +++ b/src/mailserver.js @@ -130,7 +130,7 @@ async function configureMail(mailFqdn, mailDomain, serviceConfig) { const allowInbound = await createMailConfig(mailFqdn); - const ports = allowInbound ? '-p 587:2587 -p 993:9993 -p 4190:4190 -p 25:2587 -p 465:2465 -p 995:9995' : ''; + const ports = allowInbound ? '-p 587:2587 -p 993:9993 -p 4190:4190 -p 25:2587 -p 172.18.0.1:2587:2587 -p 465:2465 -p 995:9995' : ''; const readOnly = !serviceConfig.recoveryMode ? '--read-only' : ''; const cmd = serviceConfig.recoveryMode ? '/bin/bash -c \'echo "Debug mode. Sleeping" && sleep infinity\'' : ''; const logLevel = serviceConfig.recoveryMode ? 'data' : 'info';