You are here: Home > Postfix on macos - 2

No Hair Github Pages

Using Postfix as MTA on MacOS - Part 2: Deprecated

launchd, launchctl, and postfix on MacOS 11 'Big Sur' and 12 'Monterey'

or

One way to enable postfix to receive mail on MacOS

**

This has been superceded by a different approach based on postmulti.

Please see Postfix as MTA on macos 2: Using postmulti

**

To receive mail, an SMTP server must be running and listening on the appropriate port. For MacOS, postfix is the default but you could install another like OpenSMTPD - which is easier to configure and setup than postfix. OTOH, using the base MacOS components is fairly convoluted. You need to:

1. stop and disable the existing postfix LaunchDaemon which stops and starts periodically,

2. create a new launchd process (LaunchAgent) which starts postfix and have postfix listen to the appropriate ports and to continue running,

3. edit postfix master.cf and main.cf for sending and receiving messages,

4. bootstrap the new LaunchAgent for debugging

5. finally, enable the new LaunchAgent

6. kickstart the new LaunchAgent

1. Stop and disable the existing postfix LaunchDaemon

In MacOS 11 Big Sur and 12 Monterey, the /System/Library/LaunchAgents and /System/Library/LaunchDaemons are in the protected volume and changes to these are prevented by System Integrity Protection (SIP). You can stop the initialized postfix LaunchDaemon. You can disable it with launchctl. But, you can't edit the postfix plist. The disabled postfix LaunchDaemon will still be started at the next boot because it is in the override database. To change this override you need to boot into recovery mode (to bypass SIP) and edit the override database plist. Or, you need to write a shell script that unloads and disables the default postfix LaunchDaemon then starts your new replacement postfix LaunchAgent at boot.

We will do that later, but now to setup and debug our new postfix LaunchAgent, lets stop then disable the default postfix LaunchDaemon.

Check out manlaunchctl:

kill signal-name | signal-number service-target
Sends the specified signal to the specified service if it is running. The signal number
or name (SIGTERM, SIGKILL, etc.) may be specified.
enable | disable service-target
Enables or disables the service in the requested domain. Once a service is disabled, it
cannot be loaded in the specified domain until it is once again enabled. This state
persists across boots of the device. This subcommand may only target services within
the system domain or user and user-login domains
.

You can check the status of system LaunchAgents and LaunchDaemons with

sudo launchctl print system

To stop the default postfix LaunchDaemon reversibly:

sudo launchctl kill SIGTERM system/com.apple.postfix.master

To disable the default postfix LaunchDaemon reversibly:

sudo launchctl disable system/com.apple.postfix.master

Now, recheck whats enabled and disabled:

sudo launchctl print system | grep postfix

Yielding

...
disabled services = {
{
	...
	"com.apple.postfix.master" => true
	...
	}
}

"true" means disabled and "false" means enabled. Disabling the default postfix LaunchDaemon does not unload the program. It has been initialized by launchd and will still start on the next boot when it will be enabled and started again. But for now, it's off and will stay off until the next boot or reboot.

2. Create a new LaunchAgent for postfix

You can't edit the existing one as it is located in the /System/Library/ which is part of the read-only system volume. So, make a copy in /Library/LaunchAgents.

sudo cp /System/Library/LaunchDaemons/com.apple.postfix.master.plist /Library/LaunchAgents/org.postfix.custom.plist

[As an aside, the function of the various daemons and agents managed by launchd is determined by configuration files in the following folders:

/System/Library/LaunchDaemons: Apple-supplied system daemons (start on boot)

/System/Library/LaunchAgents:- Apple-supplied agents that apply to all users on a per-user basis (which start on any user login)

/Library/LaunchDaemons: User or third-party system daemons (start on boot)

/Library/LaunchAgents: User or third-party agents that apply to all users on a per-user basis (start on any user login)

~/Library/LaunchAgents: Third-party agents that apply only to the logged-in user (start on user login)]

Edit our new org.postfix.custom.plist:

sudo nano -w /Library/LaunchAgents/org.postfix.custom.plist

Like so:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>org.postfix.custom</string>
        <key>Program</key>
        <string>/usr/libexec/postfix/master</string>
        <key>ProgramArguments</key>
        <array>
                <string>master</string>
                <string>-c</string>
                <string>/private/etc/postfix.custom/</string>
        </array>
        <key>KeepAlive</key>
        <true/>
</dict>
</plist>

So, basically, this starts postfix using the configuration file in the config directory /private/etc/postfix.custom/, and keeps it running.

3. Create the alternate config directory and edit master.cf and main.cf.

The configuration files for postfix are found in /private/etc/postfix. We will not change that as we may want to go back to the default setup easily. So, we make a new directory copying all the config files.

sudo cp -R /private/etc/postfix /private/etc/postfix.custom

Now backup the main.cf file:

sudo cp /private/etc/postfix.custom/main.cf /private/etc/postfix.custom/main.cf.default

Edit /private/etc/postfix/main.cf: Just go to the bottom of the file and add your configuration directives.

sudo nano -w /private/etc/postfix.custom/main.cf

Comment out this line at end of main.cf

# Apple defaults
...
#inet_interfaces = loopback-only

Add these line below the Apple defaults:

myhostname = hostname.domain.tld
inet_interfaces = all

No change is necessary to/private/etc/postfix/master.cf but back the default file up in case you decide to experiment.

sudo cp /private/etc/postfix.custom/master.cf /private/etc/postfix.custom/main.cf.default

4. 'Bootstrap' org.postfix.custom: This identifies and registers it as system launchd process:

sudo launchctl bootstrap system /Library/LaunchAgents/org.postfix.custom.plist

See man launchctl:

bootstrap | bootout domain-target [service-path service-path2 ...] | service-target

Bootstraps or removes domains and services. When service arguments are present, bootstraps
and correspondingly removes their definitions into the domain.  Services may be specified
as a series of paths or a service identifier.  Paths may point to XPC service bundles,
launchd.plist(5) s, or a directories containing a collection of either. If there were one
or more errors while bootstrapping or removing a collection of services, the problematic
paths will be printed with the errors that occurred.

If no paths or service target are specified, these commands can either bootstrap or remove
a domain specified as a domain target. Some domains will implicitly bootstrap pre-defined
paths as part of their creation.
			  
Note: For instance, when referring to a service with the identifier com.apple.example
loaded into the GUIdomain of a user with UID 501, domain-target is gui/501/,service-name
is com.apple.example, and service-target is gui/501/com.apple.example.

5. Enable service

sudo launchctl enable system/org.postfix.custom

Doing this marks the LaunchAgent as active/executable. This means it will be started on boot/reboot, if so configured. Like disabling, enabling persists across reboots. You can check if the service is enabled:

sudo launchctl print system | grep postfix

Check which are enabled ("false") or disabled "true")

sudo launchctl print-disabled system

6. Start service:

sudo launchctl kickstart system/org.postfix.custom

7. Check if running:

sudo postfix status

or

ps aux | grep postfix

8. Test by sending a message from a network computer:

mail -s "Test message from alpha" joe@mac.domain.tld
This is a test of postfix recieving emails from lan computer
Peace
^D
EOT

9. Now check if the message was received by your Mac in terminal:

mail

Now, you're set.

**

Sections below are incorrect/under testing - needs to be revised.

**

But if you reboot or boot, the default postfix LaunchAgent will be restarted. The new custom postfix process should also be started. Check as above. So, kill the default process:

launchctl kill SIGTERM system/com.apple.postfix.master
launchctl disable system/com.apple.postfix.master

If you want to prevent this, you can write a script to run at boot, like so:

#!/bin/sh
if [ "$(launchctl print-disabled system | grep com.apple.postfix | grep -o '[^ ]*$')" = 'true' ];
 then
        #Default postfix disabled -> nop
        exit 0;

 else
        #Default postfix enabled - switch to postfix.custom
        #Killing and disabling default postfix service
        launchctl kill SIGTERM system/com.apple.postfix.master
        launchctl disable system/com.apple.postfix.master
        #Enabling and starting postfix.custom
        launchctl enable system/org.postfix.custom
        launchctl kickstart system/org.postfix.custom
        exit 0;
fi

and another to run at shutdown:

#!/bin/sh
if [ "$(launchctl print-disabled system | grep com.apple.postfix | grep -o '[^ ]*$')" = 'true' ];
 then
        #Default postfix disabled
	    launchctl kill SIGTERM system/org.postfix.custom
        launchctl disable system/org.postfix.custom
        exit 0;

 else
        #Default postfix enabled -> nop
        exit 0;
fi
Now reboot and see that the default postfix LaunchDaemon is not running and the new custom postfix LaunchAgent is, and that it is working as expected.

Revert to default postfix configuration

sudo launchctl disable system/org.postfix.custom
sudo launchctl enable system/com.apple.postfix.master

Then, disable the post-boot and pre-shutdown scripts as above.

Of course, this is totally insecure and so 1970, but for quick messages in a trusted network or to receive messages sent from, say, a firewall or mailserver, it's ok. It's also useful to debug your setup as you are building a mail server prior to bolting on Dovecot, SASL, DKIM, DMARc, etc, etc.

However, a more typical solution is to spin up a local mailserver on the network and have the messages from the hosts/appliances forward to that machine to be checked as desired. That can be combined with a loghost for a single point of reporting and management which can be checked from local or remote workstations by the admins.


Posted by Gordon, No Hair Github Pages, April 12, 2020; revised April 20, 2022.

© nohair.net and the author

For comments, corrections, and addenda, email: gordon[AT]baskin.network

Github Pages index