Skip to content

mail 2/4: agent mail service group — Postfix/Dovecot/OpenDKIM - #503

Merged
cmyers-mieweb merged 1 commit into
mainfrom
cmyers_mail-2-agent
Nov 3, 2026
Merged

cmyers-mieweb merged 1 commit into
mainfrom
cmyers_mail-2-agent

Conversation

@cmyers-mieweb

Copy link
Copy Markdown
Collaborator

Part 2 of 4 splitting #495 (feature issue: #67).

Adds the agent-side mail service:

  • Service groups via AGENT_SERVICES
  • Mail service group managing Postfix, Dovecot, and OpenDKIM
  • Config templates for the mail stack (SNI maps, SQL maps, DKIM tables)
  • Only stops mail units this agent started
  • agent.md docs updates

Merge order: after #502 (manager backend).

Split 2/4 of #495. Adds the agent-side mail service:
- Service groups via AGENT_SERVICES
- Mail service group managing Postfix, Dovecot, and OpenDKIM
- Config templates for mail stack (SNI, SQL maps, DKIM tables)
- Only stops mail units this agent started

Merge after the manager backend PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved flaws permit cross-domain DKIM impersonation and plaintext relay credential exposure, while invalid PostgreSQL settings cause Dovecot configuration validation to fail.

14 open issues
What changed in this PR

Adds the agent-side mail stack as the second part of the email feature, building on the manager backend in #502 alongside existing nginx and dnsmasq management.

Changes:

  • Adds opt-in service groups, dependency reporting, and persisted mail lifecycle tracking.
  • Introduces Postfix, Dovecot, OpenDKIM, and mail-helper configuration, provisioning, and mailbox retention handling.
  • Extends configuration application with ownership reconciliation and post-write commands, plus tests, packaging suggestions, and documentation.
File Description
mie-opensource-landing/​docs/​developers/​agent.md Documents service groups, mail settings, and apply behavior.
agent/​test/​mail-template.test.js Tests rendered mail configuration directives.
agent/​test/​config.test.js Tests service-group parsing and validation.
agent/​templates/​mail/​postfix-vdomains.ejs Lists domains accepting inbound mail.
agent/​templates/​mail/​postfix-sql-senders.cf.ejs Configures sender-ownership lookups.
agent/​templates/​mail/​postfix-sql-mailboxes.cf.ejs Configures recipient-existence lookups.
agent/​templates/​mail/​postfix-sni-map.ejs Maps domains to TLS key and certificate files.
agent/​templates/​mail/​postfix-master.cf.ejs Defines SMTP listeners and submission policies.
agent/​templates/​mail/​postfix-main.cf.ejs Configures delivery, TLS, authentication, and milters.
agent/​templates/​mail/​opendkim.conf.ejs Configures DKIM signing and socket access.
agent/​templates/​mail/​opendkim-signingtable.ejs Maps sender domains to signing keys.
agent/​templates/​mail/​opendkim-keytable.ejs Maps DKIM selectors to private-key files.
agent/​templates/​mail/​dovecot.conf.ejs Configures IMAP, SQL authentication, LMTP, and quotas.
agent/​src/​types.ts Defines mail snapshots and service-reporting fields.
agent/​src/​system.ts Adds binary checks, ownership helpers, and unit lifecycle operations.
agent/​src/​state.ts Persists mail management responsibility.
agent/​src/​mail.ts Implements mail rendering, provisioning, lifecycle, and mailbox cleanup.
agent/​src/​index.ts Integrates enabled groups and mail housekeeping into check-ins.
agent/​src/​config.ts Parses and validates AGENT_SERVICES.
agent/​src/​apply.ts Adds service groups, file ownership, and post-write processing.
agent/​.fpm Suggests optional mail-stack packages.

Review effort: Balanced

Comment on lines +32 to +40
<%= db.dovecotDriver %> <%= db.dovecotHost %> {
<%_ if (db.dovecotParams.dbname) { _%>
dbname = <%= db.dovecotParams.dbname %>
<%_ } _%>
user = <%= db.dovecotUser %>
<%_ if (db.dovecotPassword) { _%>
password = <%= db.dovecotPassword %>
<%_ } _%>
}
@@ -0,0 +1,3 @@
<%_ mail.dkim.forEach((k) => { _%>
*@<%= k.domain %> <%= k.selector %>._domainkey.<%= k.domain %>
tls_server_sni_maps = hash:/etc/postfix/sni_map
smtpd_tls_chain_files = <%= certs.fallbackKey %>, <%= certs.fallbackCert %>
smtpd_tls_security_level = may
smtp_tls_security_level = may
Comment thread agent/src/index.ts
Comment on lines +73 to +75
const mailManaged = await syncMailUnits(config, state.mailManaged);
if (mailManaged !== state.mailManaged) {
state.mailManaged = mailManaged;
Comment thread agent/src/index.ts
if (cfg.services.includes('mail') && result.config.mail) {
ensureMailProvisioned(cfg);
}
for (const svc of enabledServices(cfg)) {
Comment thread agent/src/mail.ts
Comment on lines +324 to +327
if (states.some((state) => state === 'active' || state === 'activating')) {
log.info('mail: service group disabled but units are running; stopping them');
await stopAndDisableServices(MAIL_UNITS);
}
Comment thread agent/src/system.ts
export async function stopAndDisableServices(units: string[]): Promise<void> {
const mgr = await getManager();
for (const unit of units) {
await runJob('StopUnit', unit).catch(() => undefined);
<%_ } _%>
user = <%= db.dovecotUser %>
<%_ if (db.dovecotPassword) { _%>
password = <%= db.dovecotPassword %>
Comment on lines +37 to +43
virtual_mailbox_maps = <%= db.prefix %>:/etc/postfix/sql/mailboxes.cf
virtual_transport = lmtp:unix:private/dovecot-lmtp
smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination
smtpd_recipient_restrictions = reject_unauth_destination, check_policy_service unix:private/quota-status

# Sender ownership: submission enforces sender == SASL login (live from SQL).
smtpd_sender_login_maps = <%= db.prefix %>:/etc/postfix/sql/senders.cf
hosts = <%= db.hosts %>
user = <%= db.user %>
<%_ if (db.password) { _%>
password = <%= db.password %>

@runleveldev runleveldev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pretty extensively reviewed this section in the original PR, so no further comments.

@cmyers-mieweb
cmyers-mieweb merged commit 7c32b0a into main Nov 3, 2026
9 checks passed
@cmyers-mieweb
cmyers-mieweb deleted the cmyers_mail-2-agent branch November 3, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants