We want to automount several NFS shares from a server on our local network at /Network/{Applications,Library,Users,share,usrlocal}
in the Mac file system. Furthermore, we want this to work with a MacBook running macOS 13.1, regardless of whether it is on the local network or not (meaning, the system should not hang for inordinate periods of time when not on the local network).
- We have at least two Network Locations:
Automatic
(left at macOS defaults)Home
- With the
Home
Network Location, we've arranged (e.g. using local DNS) that:- the MacBook's host name is
macbook.example.net
, - the NFS server is reachable at
nfs.example.net
, - the NFS server is also reachable at
tbt-nfs.example.net
if directly connected to the MacBook via Thunderbolt networking connection. This secondary address is optional, used to obtain the highest speed connection possible when docked at the desk where the NFS server is located.
- the MacBook's host name is
- With any other Network Location, the Macbook has a different host name.
- We want to keep System Integrity Protection (SIP) enabled.
With SIP enabled, modern macOS security policy ensures that:
automountd
cannot mount directly on/Network
. This rules out the possibility of describing/Network
using an indirect automount map, or similar. So instead we'll use a direct map separately describing each mount point under/Network
.automountd
cannot mount directly on/Network/Library
. To work around this, we'll have the automounter mount on/Network/.Library
instead, then provide a symbolic link from/Network/Library
to/Network/.Library
.
To create the /Network/.Library
mount point and /Network/Library
link(s):
- Reboot into Recovery Mode
cd /Volumes/Data
mkdir -p Network/.Library
ln -s .Library Network/Library
Create private/etc/synthetic.conf
, containing the line:
Network System/Volumes/Data/Network
and then reboot.
Note that automountd
would normally create the synthetic link [6] from /Network
to /System/Volumes/Data/Network
as required on modern macOS with ROSV (Read-only System Volume), but creating the Network
directory manually as needed to provide the Library
symlink prevents this from happening, so we create it manually.
Starting with the macOS default /etc/auto_master
[2]:
#
# Automounter master map
#
+auto_master # Use directory service
#/net -hosts -nobrowse,hidefromfinder,nosuid
/home auto_home -nobrowse,hidefromfinder
/Network/Servers -fstab
/- -static
We create a new automount direct map [1] file /etc/auto_Network
:
/System/Volumes/Data/Network/Applications tbt-nfs,nfs:/Exports/Local/MacOSX/Applications
/System/Volumes/Data/Network/.Library tbt-nfs,nfs:/Exports/Local/MacOSX/Library
/System/Volumes/Data/Network/Users tbt-nfs,nfs:/Exports/Users
/System/Volumes/Data/Network/usrlocal tbt-nfs,nfs:/Exports/Local/MacOSX/usrlocal
/System/Volumes/Data/Network/share tbt-nfs,nfs:/Exports/Local/share
(with client-side NFS failover configured to try tbt-nfs.example.net
first, then nfs.example.net
).
We then add the following line to end of /etc/auto_master
to enable this automount map:
/- auto_Network -soft,intr,retrycnt=0,retrans=1,dumbtimer,timeo=1,deadtimeout=1
Alternatively, we can create auto_master
and auto_Network
maps in Open Directory (LDAP) instead of making the file modifications above. This way we don't have to repeat the configuration if we need to support mulitple NFS client machines using the same Network account server:
To do this, we create ~/Documents/automount.ldif
[7] (assuming Open Directory server is ldap.example.net
at 192.168.1.1
; replace dc=ldap,dc=example,dc=net
and 192.168.1.1
below with the appropriate values for your server) with the following contents:
# auto_master
dn: automountMapName=auto_master,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: top
objectClass: automountMap
automountMapName: auto_master
description: Must be initialized from ~/Documents/automount.ldif using ldapadd(1)
# auto_master: /- auto_Network
dn: automountKey=/-,automountMapName=auto_master,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: auto_Network -bg,soft,intr,retrycnt=0,retrans=1,dumbtimer,timeo=1,deadtimeout=1
automountKey: /-
description: auto_master entry for auto_Network map
# auto_Network
dn: automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: top
objectClass: automountMap
automountMapName: auto_Network
description: auto_Network map
# auto_Network: /System/Volumes/Data/Network/Applications tbt-nfs,nfs:/Exports/Local/MacOSX/Applications
dn: automountKey=/System/Volumes/Data/Network/Applications,automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: tbt-nfs,nfs:/Exports/Local/MacOSX/Applications
automountKey: /System/Volumes/Data/Network/Applications
description: Applications entry of auto_Network map
# auto_Network: /System/Volumes/Data/Network/Users tbt-nfs,nfs:/Exports/Users
dn: automountKey=/System/Volumes/Data/Network/.Library,automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: tbt-nfs,nfs:/Exports/Local/MacOSX/Library
automountKey: /System/Volumes/Data/Network/.Library
description: .Library entry of auto_Network map
# auto_Network: /System/Volumes/Data/Network/Users tbt-nfs,nfs:/Exports/Users
dn: automountKey=/System/Volumes/Data/Network/Users,automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: tbt-nfs,nfs:/Exports/Users
automountKey: /System/Volumes/Data/Network/Users
description: Users entry of auto_Network map
# auto_Network: /System/Volumes/Data/Network/usrlocal tbt-nfs,nfs:/Exports/Local/MacOSX/usrlocal
dn: automountKey=/System/Volumes/Data/Network/usrlocal,automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: tbt-nfs,nfs:/Exports/Local/MacOSX/usrlocal
automountKey: /System/Volumes/Data/Network/usrlocal
description: usrlocal entry of auto_Network map
# auto_Network: /System/Volumes/Data/Network/share tbt-nfs,nfs:/Exports/Local/share
dn: automountKey=/System/Volumes/Data/Network/share,automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net
objectClass: automount
automountInformation: tbt-nfs,nfs:/Exports/Local/share
automountKey: /System/Volumes/Data/Network/share
description: share entry of auto_Network map
We then (re)load this into LDAP:
ldapdelete -r -h 192.168.1.1 -U diradmin -W -v 'automountMapName=auto_master,cn=automountMap,dc=ldap,dc=example,dc=net' 'automountMapName=auto_Network,cn=automountMap,dc=ldap,dc=example,dc=net'
ldapadd -h 192.168.1.1 -U diradmin -W -v -f ~/Documents/automount.ldif
Have I mentioned how much I hate LDAP? Well, now I have.
In case you're wondering, it appears this cannot be accomplished using either dscl(1)
or Directory Utility
, unfortunately[8].
The MacBook is a mobile device. With the configuration above, when we're not connected to the local network (where our NFS server is unavailable), we'll have problems with long macOS UI hangs due to automount RPC timeouts (e.g. when passing references to /Network/Library
are made during login). This stems from the persistence of the automount maps regardless of Network Location and a hardcoded RPC timeout in automountd.
Note: I had originally thought that, by virtue of using Open Directory (OD), I could easily avoid having automount maps defined with the Automatic
Network Location, even when connected to my local Wi-Fi network where the OD server is reachable. However, since the Directory Services configuration apparently ignores Network Location on modern macOS, and since I've configured Directory Services using the IP address of the OD server (192.168.1.1
), the MacBook still references the Open Directory server and obtains the automount maps with the Automatic
Network Location, even though it can't resolve the DNS addresses of the NFS server referenced in the maps. And I was unwilling to change the automount maps to specify the NFS server IP addresses directly, or change my Wi-Fi DHCP configuration, for various reasons.
To mitigate this, I had originally hoped to reduce automountd's RPC timeout period. Since it is hardcoded, this would require installing a locally rebuilt automountd
(which is possible, though much more work than one might expect, given automountd
has dependencies on many closed-source private APIs--who said Darwin is Open Source?). However, this plan was ultimately thwarted by SIP, which essentially makes it impossible to replace the stock automountd
as long as SIP is enabled (it's possible to unload the stock automountd
with SIP disabled, but that change is disregarded as soon as SIP is re-enabled.).
So with these constraints, it seems the best we can do is to try to explicitly avoid having the automount maps defined outside the Home
Network Location.
As long as we can guarantee a unique hostname will be used with the Home
Network Location, and that the NFS server on the local network remains generally available, we can mitigate this problem by
- using a hostname-specific direct automount map (using the
$HOST
variable inauto_master
); - invoking
automount -vc
to reload the automountd configuration whenever the Network Location is changed.
Starting with the LDAP-based configuration above, we could make the following changes to /etc/auto_master
:
- comment out
+auto_master
(don't includeauto_master
map from Open Directory) - add static map
auto_$HOST
Here's the new /etc/auto_master
:
#
# Automounter master map
#
#+auto_master # Use directory service
#/net -hosts -nobrowse,hidefromfinder,nosuid
#/home auto_home -nobrowse,hidefromfinder
/Network/Servers -fstab
/- -static
/- auto_$HOST -soft,intr,retrycnt=0,retrans=1,dumbtimer,timeo=1,deadtimeout=1
We then create /etc/auto_macbook.example.net
containing one line:
+auto_Network # include auto_Network map from directory service
Alternatively, if not using LDAP, we could simply rename the /etc/auto_Network
file previously described above to /etc/auto_macbook.example.net
.
We want to run automount -vc
whenever the Network Location is changed, so that this works as intended. We can do that with a launchd.plist(5)
, as follows:
Create /Library/LaunchDaemons/net.example.automounts.plist
(replacing net.example
with your own prefix below):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.example.automounts</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/automount</string>
<string>-vc</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/SystemConfiguration/preferences.plist</string>
<string>/etc/auto_master</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Then load it:
sudo launchctl load /Library/LaunchDaemons/net.example.automounts.plist
- Autofs: Automatically Mounting Network File Shares in Mac OS X (Autofs.pdf)
auto_master(5)
automount(8)
automountd(8)
autofs.conf(5)
synthetic.conf(5)
ldif(5)
- https://superuser.com/questions/621147/unable-to-add-automount-entries-on-macosx-using-directory-utility