Skip to content

fix(mail): reconcile mounted Let's Encrypt certificates into daemon paths on boot - #838

Merged
Hydralerne merged 3 commits into
oblien:feat/bugfix-integration-2026-09-15from
lagmandu:fix/mail-ssl-reconcile
Sep 15, 2026
Merged

Hydralerne merged 3 commits into
oblien:feat/bugfix-integration-2026-09-15from
lagmandu:fix/mail-ssl-reconcile

Conversation

@lagmandu

@lagmandu lagmandu commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Postfix and Dovecot inside the \openship-mail\ container load certificates from /etc/ssl/certs/iRedMail.crt\ and /etc/ssl/private/iRedMail.key.

Because /etc/ssl\ is inside the ephemeral container layer (not a persistent volume), recreating the container or updating the image reverts /etc/ssl\ to the baked self-signed certificates — even when valid certificates are mounted at /etc/letsencrypt.

As a result, applications and clients connecting via STARTTLS on port 587 or SSL on 465 (like Node.js / nodemailer, Python, or mail clients) fail with:
\
Error: self-signed certificate (code: ESOCKET, command: CONN)
\\

This PR adds a reconciliation step (\3c) to \�pps/email/docker/entrypoint.sh\ that checks for \mail.\ (or \) under /etc/letsencrypt/live/\ on every boot and symlinks \ ullchain.pem\ and \privkey.pem\ into the default daemon certificate paths.

Fixes #837

Verification

  • Added and ran unit tests: \�pps/api/test/lib/mail-ssl-reconcile.test.ts\
  • Formatted with Prettier
  • Tested certificate reconciliation and TLS handshake with \openssl s_client -connect 127.0.0.1:587 -starttls smtp\ verifying \Verify return code: 0 (ok)\
  • Verified Node.js client (
    odemailer.createTransport().verify()) succeeds without certificate rejection

…aths on boot

Postfix and Dovecot inside the openship-mail container load certificates from /etc/ssl/certs/iRedMail.crt and /etc/ssl/private/iRedMail.key. Because /etc/ssl is ephemeral, container recreations and image updates revert to the baked self-signed certificates even when valid certificates are mounted at /etc/letsencrypt.

This reconciles /etc/letsencrypt certificates on every boot into the daemon paths if present, ensuring TLS clients never fail with self-signed certificate errors.

Fixes oblien#837
@Hydralerne
Hydralerne changed the base branch from main to feat/bugfix-integration-2026-09-15 September 15, 2026 18:26
@Hydralerne

Hydralerne commented Sep 15, 2026

Copy link
Copy Markdown
Member

Updated this PR branch for the current architecture and retargeted it to feat/bugfix-integration-2026-09-15. The original contribution remains in the commit history.

The startup repair now validates the certificate hostname, validity period, server usage, and matching private key before switching iRedMail to the mounted certificate. It repairs the certificate and key independently, follows stable Let’s Encrypt live paths for renewal, and retains the fallback files when no valid mounted pair exists.

Validation: 10 Docker tests passed with real OpenSSL certificates and a TLS handshake, covering stale key links, repeat starts, renewal, container recreation, invalid certificates, and apex SAN/wildcard certificates. API typechecking passed. CI also exposed two older integration test fixture problems; those were corrected on the integration branch and merged into this PR. All CI jobs passed in run 35008541433. This original PR is now merged into the bug-fix branch through 1a4f535, awaiting the eventual merge to main.

@Hydralerne
Hydralerne merged commit 1a4f535 into oblien:feat/bugfix-integration-2026-09-15 Sep 15, 2026
9 checks passed
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.

[Bug] openship-mail: Postfix and Dovecot fall back to self-signed certificate when Let's Encrypt cert is mounted

2 participants