-
-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FR: Allow file based communication (sendmail-like/unix socket; no networking/ports) #373
Comments
Thanks for your detailed feature request @RafaelKr ~ very useful. This is a very interesting idea as it does provide an alternate approach to the whole "port issue" ~ which has been raised a couple of times before by others implementing Mailpit in large multi-user/customer environments with shared hosting. Forgetting briefly that Mailpit is not limited to just *nix platforms (ie: I do not believe unix sockets are supported on Windows)..... theoretically this idea could work. I just did a (very crude) test running the Mailpit HTTP server via a unix socket which Nginx connected directly to (as per your example) and it worked great (including the websocket). But ... I'm not sure about the SMTP server because the mhale/smtpd module (that Mailpit uses) does not currently support sockets (it's hardcoded to use tcp). I would need to fork and test to see if that would even work with a unix socket. Assuming it all worked, to implement this properly will be quite a lot of work as there are several moving parts, including of course the SMTP server & SMTP client, plus testing & of course documentation. I'm not sure when I can make the time available as I'm scheduled to have shoulder surgery in about 6 weeks time which will put me "out of action" for a couple more months. To give me some idea of your need for this feature, could you please tell me what kind of scale are you wanting to use this for (ie: how many projects would be using this if it was implemented), and how & if you (or your company) could potentially contribute? Thanks. |
@RafaelKr I have started work on this feature and it seems possible using HTTP & SMTPD sockets instead of TCP, as well as the Mailpit "sendmail" sending directly via the SMTPD socket- so it will be possible to run multiple instances of Mailpit without using any TCP ports. It does require quite a number of internal Mailpit changes, as well as some heavy modifications to the SMTPD library (meaning I will need to bundle the modified library into Mailpit's source code), and I'll still need to do a lot of testing - so there's a lot more work for me to do. It would still be great if you could get back to me on my previous questions, thank you. |
@axllent Wow, didn't expect it to go that fast after your "I'm not sure when I can make the time available" response, but I'm positively surprised! First of all, I wish you all the best for the surgery, I hope everything goes well. So to your questions:
But I just came up with another idea that wouldn't require modifications to the smtpd library. When we request address "localhost:0" the OS automatically assigns a free port, so we also don't have collisions. I will try to come up with a quick PoC if we can get the port from smtpd then. |
I got a working PoC. Here's the code: https://gist.github.com/RafaelKr/ff101e0b505caf4150146a34e569e1a4 Usage: mkdir mailpit-smtpd-port-poc
cd mailpit-smtpd-port-poc
go mod init smtpd-port-poc
# Download GitHub gist PoC snippet
curl -O https://gist.githubusercontent.com/RafaelKr/ff101e0b505caf4150146a34e569e1a4/raw/814f60c56e4510d78a5e2b3a4407317773930f10/mailpit-smtpd-port-poc.go
go get github.com/mhale/smtpd
# Start Server
go run mailpit-smtpd-port-poc.go Now we got a server running on a free port chosen by the system. Also a new shell wrapper script In a new shell (running in the project dir) we can now create a mail.txt file: printf 'Subject: Terminal Email Send\n\nEmail Content' > mail.txt And send mails via ./mailpit-sendmail.sh [email protected] < mail.txt Now we should see a log line in the shell where the server is running. |
I also just checked if that works inside a Symfony project. It does! MAILER_DSN="sendmail://default?command=/path/to/mailpit-smtpd-port-poc/mailpit-sendmail.sh%20-bs" bin/console mailer:test [email protected] And I got a log!
Unfortunately I can only test on Linux, I'm pretty sure it will also work on macOS - but I don't know if or how it will work on Windows. Edit: I used the
|
Hey @RafaelKr - thanks for both the sponsorship and the thought you have put into this! It's really useful to have two brains thinking of a solution instead of one 😄 Whilst your approach and PoC for a dynamic port plus a "sendmail wrapper" is an interesting one (and which works), I personally do not think it's the right solution as it still requires TCP which is not always ideal - I'll explain my thinking:
So the goal I have here is to be able to:
I'll keep working on this (I'm trying to get it done as soon as possible because of the planned surgery), and I'm fairly sure I can get something out within the next week or two that will allow you to start testing the new experimental feature 👍 |
Just to chime in here with a very little detail: postfix uses LMTP delivering mail to local mailboxes, AFAIK LMTP is simply an smtp implementation over local sockets, maybe something like this also exists in Go. If this comment is not fitting or does not help, in this situation, please just ignore ;-) |
Thank you for the "chime in" @Corvan - I'm always open to ideas ;-) Until now I hadn't actually even heard of LMTP. I've done some reading but cannot see any advantages (for what I'm trying to achieve here) over SMTP. LMTP (which uses a slightly different "language" compared to SMTP) apparently works by default over TCP on port 24 and is designed for local mail delivery which doesn't use authentication like SMTP does. Some examples I saw (Dovecot + postfix) appear to use a Unix socket to communicate, so that must be an option too, however I strongly suspect that this is very similar to the current implementation I'm working on. Whilst I appreciate your suggestion, however I'm going to pursue my previous approach for now as that appears to be working so far as expected without needing to introduce another communication dialect or libraries (only to bundle a modified version of one). The way I see it, being able to configure Mailpit's SMTP server to listen on a Unix socket (and its sendmail implementation to communicate with that socket) will enable large-scale and automated setups with less headaches. This approach would for instance allow Docker containers to communicate between each other (or the host) without needing to know IP addresses or reserve TCP ports in advance. Of course everyone's setup is different, so this is likely for a very niche (but potentially large) "market". |
@axllent sure, go as you see fit, my thought was only, that you might be able to piggyback, but if course knowing that it could come with different repercussions and dependency. Just wanted to mention it, to have it mentioned, with my fractured knowledge of past times 😉 |
I am just in the process of releasing v.1.21.0 which includes this experimental feature. I say "experimental" as it's currently undocumented and pending testing from you (please). The new syntax for setting a Unix socket instead of a TCP address for either (or both of) the HTTP and/or SMTP is
This applies to environment variables too if you use those instead. To use Mailpit's "sendmail" to send via the socket, you should use the same syntax (but without the permissions), so in this example:
It should also work (although I did not test it yet) with the Please let me know how this works for you, thanks 😄 |
Awesome, thank you so much! Unfortunately I'm currently ill, I will look into it as soon as I'm feeling good again. |
Sorry to hear that, and not a problem. Get better soon! |
I tested it inside my project using devenv.sh and it just works! 🎉 Also I built a little demo using Symfony + devenv: https://github.com/RafaelKr/mailpit-socket-devenv |
That's awesome @RafaelKr, thanks for the feedback! I started writing some documentation yesterday but got stuck on proxying using Apache. It seems Apache can proxy to a Unix socket, however I'm not having any luck proxying the websocket (I suspect this may just not be supported). I'm going to leave this ticket open for now until I can release some documentation. |
@RafaelKr I finally worked it out (Apache) which requires a recent version of Apache and a different config syntax than the one I am used to 🥳 I'm not even going to try the other proxies (Trafiek, caddy etc) as I have no experience with them.... anyway... Please take a look at the documentation I've written with an example for both Nginx, Apache and "sendmail", and feel free to send a PR (to the website project) if you feel there is anything I should add or change. I have stated that this feature is experimental, both in the Mailpit release notes as well as on the website, but rest assured I won't be removing it. It just needs a lot more testing "in production" before I can state it's no longer experimental, so I would really appreciate your feedback (once you have started using this method in your infrastructure) to whether this has made it much simpler for you, and of course any issues. I'll close this issue now, but feel free to comment on it when you can. |
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [axllent/mailpit](https://github.com/axllent/mailpit) | minor | `v1.20.7` -> `v1.21.4` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>axllent/mailpit (axllent/mailpit)</summary> ### [`v1.21.4`](https://github.com/axllent/mailpit/blob/HEAD/CHANGELOG.md#v1214) [Compare Source](axllent/mailpit@v1.21.3...v1.21.4) ##### Bugfix - Fix external CSS stylesheet loading in HTML preview ([#​388](axllent/mailpit#388)) ### [`v1.21.3`](https://github.com/axllent/mailpit/blob/HEAD/CHANGELOG.md#v1213) [Compare Source](axllent/mailpit@v1.21.2...v1.21.3) ##### Chore - Update Go dependencies - Minor UI tweaks - Mute Dart Sass deprecation notices - Update node dependencies - Upgrade Alpine packages on Docker build - Add swagger examples & API code restructure ### [`v1.21.2`](https://github.com/axllent/mailpit/blob/HEAD/CHANGELOG.md#v1212) [Compare Source](axllent/mailpit@v1.21.1...v1.21.2) ##### Feature - Add additional ignored flags to sendmail ([#​384](axllent/mailpit#384)) ##### Chore - Remove legacy Tags column from message DB table - Update Go dependencies - Update node dependencies ##### Fix - Fix browser notification request on Edge ([#​89](axllent/mailpit#89)) ### [`v1.21.1`](https://github.com/axllent/mailpit/blob/HEAD/CHANGELOG.md#v1211) [Compare Source](axllent/mailpit@v1.21.0...v1.21.1) ##### Feature - Add ability to search by size smaller or larger than a value (eg: `larger:1M` / `smaller:2.5M`) - Add ability to search for messages containing inline images (`has:inline`) ##### Chore - Update Go dependencies - Separate attachments and inline images in download nav and badges ([#​379](axllent/mailpit#379)) ### [`v1.21.0`](https://github.com/axllent/mailpit/blob/HEAD/CHANGELOG.md#v1210) [Compare Source](axllent/mailpit@v1.20.7...v1.21.0) ##### Feature - Experimental Unix socket support for HTTPD & SMTPD ([#​373](axllent/mailpit#373)) ##### Fix - Allow multiple item selection on macOS with Cmd-click ([#​378](axllent/mailpit#378)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40NDAuNyIsInVwZGF0ZWRJblZlciI6IjM3LjQ0MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Use case summary
Use case 1: Starting up many local projects in parallel
I'm working at an agency and sometimes we need to spin up multiple projects (and mailpit instances) in parallel.
We're using https://devenv.sh/ to configure our projects, so we just have to set
services.mailpit.enable = true
in our devenv.nix file and have a running mailpit instance. Data is stored in./.devenv/state/mailpit/db.sqlite
, the path is relative to the project. Also see https://github.com/cachix/devenv/blob/d612b77ff73912cd82e58256ab5e84d5904abef7/src/modules/services/mailpit.nix#L47 and https://devenv.sh/reference/options/#servicesmailpitenableThe mailpit instance listens on
127.0.0.1:1025
and127.0.0.1:8025
by default, so if we want to spin up more than one project in parallel we always need to temporarily change the ports of at least one project and then change them back to defaults later.I also thought of reserving 2 free ports per project, but I probably worked on 100+ projects by now. Always reserving new ports per project doesn't seem feasible, also sharing the project configuration with colleagues isn't easy then.
Use case 2: Hosting many projects on one server
We're hosting many NixOS servers, they're configured declaratively. Those servers host multiple projects as virtual hosts. Most projects have a dev and staging environment where we're using a mailpit instance per virtual host.
Currently we spin up multiple mailpit instances and always need to search for free ports for the mailpit UI and the SMTP services. Then we need to define the chosen ports in the configuration. The UI is configured in the reverse proxy to be reachable via
https://www.example.com/mailpit/
(protected via BasicAuth).Other Service Examples
For services like the Database or redis I'm able to configure them to listen via unix sockets, so we can specify them as
As you can see we can just use a generic project name (
project-1
,project-2
, ...) to automatically reserve sockets. Or in the case of the database, where only one instance is running, the context (accessible databases, database permissions, ...) depends on the system user under which the application is running.So unix sockets have another advantage from a security perspective: We can set unix permissions on them to control which user has access to them. In our server environments every vhost has an own user. The application is running as that user. And the redis instances (started via systemd) are running as the same user and a socket permission is set to 600, so only the vhost user can read and write to their own socket.
Mailpit Feature Request
It would be nice to be able to configure mailpit in a similar way.
SMTP
For the SMTP configuration it could be a sendmail-compatible wrapper file.
Configuration examples are for Symfony:
MAILER_DSN
transport syntax: https://symfony.com/doc/current/mailer.html#transport-setupMailpit UI
See: https://mailpit.axllent.org/docs/configuration/proxy/
For the reverse proxy (nginx in our case) something like:
Proposed mailpit configuration options
We need to be able to specify a socket path and also need to be able to specify the socket/mailpit-sendmail permissions. My idea would be to have the following configuration options:
MP_UI_BIND_ADDR
: allow configuration of sockets or create an own configuration option (important thing is to be able to disable networking so there will be noEADDRINUSE
errors due to port collissions)MP_UI_SOCKET_PERM
: octal permissions, see redis unixsocketperm (references below), default to600
MP_SMTP_BIND_ADDR
: see MP_UI_BIND_ADDRMP_SMTP_SOCKET_PERM
: see MP_UI_SOCKET_PERM, default to700
if it is a wrapper binary or600
if it's somehow possible to use a socketSocket owner (user) and group should be determined from the mailpit instance user and group.
References:
unixsocket
andunixsocketperm
, also needsport 0
to disable networking: https://redis.io/docs/latest/operate/oss_and_stack/management/config-file/Allow SMTP over unix socket
: Allow SMTP over unix socket (& rework mailer settings) go-gitea/gitea#18901Advanded: Abstract Namespace Sockets
Maybe interesting for an implementation of the mailpit-sendmail wrapper if it needs to be a binary instead of a socket. The to communicate with the mailpit instance process could be implemented with that.
net
package of Go already supports Abstract Namespace Sockets out of the box: https://golang-examples.tumblr.com/post/92025745979/pitfall-of-abstract-unix-domain-socket-address-inThe text was updated successfully, but these errors were encountered: