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.
This commit is contained in:
Girish Ramakrishnan
2026-04-09 15:16:18 +02:00
parent 2a2a5ffb66
commit c2ec97d641
4 changed files with 11 additions and 7 deletions

View File

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

View File

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

View File

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

View File

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