sendmail: add requiresValidCertificate

some apps really want a valid certificate to send mail and upstream
authors won't add support to skip self-signed certs or skip host name
check in cert. In our case, the issue is that we use 'mail' as the
server name despite having valid certs.

this flag will set the server name to the full mail server fqdn and
also reconfigure the app as needed when the mail server name changes.

we also set up the mail server name to resolve to internal IP because
no mail port is exposed when we are not receiving emails!
This commit is contained in:
Girish Ramakrishnan
2025-03-08 12:04:13 +01:00
parent 02666b7da4
commit 5d88e86462
6 changed files with 35 additions and 21 deletions

View File

@@ -2931,4 +2931,4 @@
* ubuntu: alert for 20.04 support being deprecated
* domains: vanity nameservers
* token: access can by restricted by ip range(s)
* sendmail: requiresValidCertificate option for using mail server domain

34
package-lock.json generated
View File

@@ -16,7 +16,7 @@
"@smithy/node-http-handler": "^4.0.2",
"@smithy/util-retry": "^4.0.1",
"async": "^3.2.6",
"cloudron-manifestformat": "^5.26.2",
"cloudron-manifestformat": "^5.27.0",
"connect": "^3.7.0",
"connect-lastmile": "^2.2.0",
"connect-timeout": "^1.9.0",
@@ -3287,26 +3287,30 @@
}
},
"node_modules/cloudron-manifestformat": {
"version": "5.26.2",
"resolved": "https://registry.npmjs.org/cloudron-manifestformat/-/cloudron-manifestformat-5.26.2.tgz",
"integrity": "sha512-o8q4HXQvYHIbZK4xftzffO2gTzw+U0797IiXzdVNszpEUkcoqeN1RNvRdAYl3vXPE92VL3MF0o5iN8tBFoWbng==",
"version": "5.27.0",
"resolved": "https://registry.npmjs.org/cloudron-manifestformat/-/cloudron-manifestformat-5.27.0.tgz",
"integrity": "sha512-wtNVemuziOCHlcMNILkRyHKMFLertkf91pzp+Qxm3KQ4MMDHyeKHFde1jO+tLbBMtWhdUGopMFTRMapDtSf+WA==",
"license": "MIT",
"dependencies": {
"cron": "^3.2.1",
"safetydance": "2.4.0",
"semver": "^7.6.3",
"cron": "^4.1.0",
"safetydance": "2.5.0",
"semver": "^7.7.1",
"tv4": "^1.3.0",
"validator": "^13.12.0"
}
},
"node_modules/cloudron-manifestformat/node_modules/safetydance": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/safetydance/-/safetydance-2.4.0.tgz",
"integrity": "sha512-KsQJ5xpuv05yeLVMP6FTp8PNtj/iY5MxmirU2Bb6lf5EvKkZFr3Qrx9umV9/NrAvRfin8mhotJNAnBD3C3MUkw==",
"engines": [
"node >= 4.0.0"
],
"license": "MIT"
"node_modules/cloudron-manifestformat/node_modules/cron": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cron/-/cron-4.1.0.tgz",
"integrity": "sha512-wmcuXr2qP0UZStYgwruG6jC2AYSO9n5VMm2t93hmcEXEjWY3S2bsXe3sfGUrTs/uQ1AvRCrZ0Pp9Q032L/V9tw==",
"license": "MIT",
"dependencies": {
"@types/luxon": "~3.4.0",
"luxon": "~3.5.0"
},
"engines": {
"node": ">=18.x"
}
},
"node_modules/co": {
"version": "4.6.0",

View File

@@ -24,7 +24,7 @@
"@smithy/node-http-handler": "^4.0.2",
"@smithy/util-retry": "^4.0.1",
"async": "^3.2.6",
"cloudron-manifestformat": "^5.26.2",
"cloudron-manifestformat": "^5.27.0",
"connect": "^3.7.0",
"connect-lastmile": "^2.2.0",
"connect-timeout": "^1.9.0",

View File

@@ -42,6 +42,7 @@ const apps = require('./apps.js'),
debug = require('debug')('box:docker'),
Docker = require('dockerode'),
fs = require('fs'),
mailServer = require('./mailserver.js'),
os = require('os'),
paths = require('./paths.js'),
promiseRetry = require('./promise-retry.js'),
@@ -413,7 +414,8 @@ async function createSubcontainer(app, name, cmd, options) {
SecurityOpt: [ 'apparmor=docker-cloudron-app' ],
CapAdd: [],
CapDrop: [],
Sysctls: {}
Sysctls: {},
ExtraHosts: []
}
};
@@ -428,7 +430,12 @@ 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 = [ `${dashboardFqdn}:172.18.0.1` ];
if (manifest.id !== 'com.adguard.home.cloudronapp') containerOptions.HostConfig.ExtraHosts.push(`${dashboardFqdn}:172.18.0.1`);
if (manifest.addons?.sendmail?.requiresValidCertificate) {
const { fqdn:mailFqdn } = await mailServer.getLocation();
containerOptions.HostConfig.ExtraHosts.push(`${mailFqdn}:${constants.MAIL_SERVICE_IPv4}`);
}
containerOptions.NetworkingConfig = {
EndpointsConfig: {

View File

@@ -248,5 +248,6 @@ async function onMailServerLocationChanged(auditSource) {
// mark apps using email addon to be reconfigured
const [, installedApps] = await safe(apps.list());
await safe(apps.configureApps(installedApps.filter((a) => !!a.manifest.addons?.email), { scheduleNow: true }, auditSource), { debug });
const appsUsingEmail = installedApps.filter((a) => !!a.manifest.addons?.email || a.manifest.addons?.sendmail?.requiresValidCertificate);
await safe(apps.configureApps(appsUsingEmail, { scheduleNow: true }, auditSource), { debug });
}

View File

@@ -1133,8 +1133,10 @@ async function setupSendMail(app, options) {
const password = existingPassword || hat(4 * 48); // see box#565 for password length
const smtpServer = options.requiresValidCertificate ? (await mailServer.getLocation()).fqdn : 'mail'; // this is also a hint to reconfigure on mail server name change
const env = [
{ name: 'CLOUDRON_MAIL_SMTP_SERVER', value: 'mail' },
{ name: 'CLOUDRON_MAIL_SMTP_SERVER', value: smtpServer },
{ name: 'CLOUDRON_MAIL_SMTP_PORT', value: '2525' },
{ name: 'CLOUDRON_MAIL_SMTPS_PORT', value: '2465' },
{ name: 'CLOUDRON_MAIL_STARTTLS_PORT', value: '2587' },