Setup NetBoot Service on Mac OS X 10.6.x Client - Setup

By: Richard Glaser - Revised: 2011-07-21 rcg

Index

Create Directories & Symlinks

Create Directories & Symlinks
Create "NetBoot" directory in "/Library" directory...

mkdir /Library/NetBoot

Create "NetBootClient0" directory in "/Library/NetBoot" directory...

mkdir /Library/NetBoot/NetBootClients0

This is where the client shadow file data is stored.

If you wanted better performance, you could setup multiple share points using different
server hard disks named...

NetBoot Client Data Sharepoint

Set Privileges
Recursively, change owner to "root" & group to "admin"...

chown -R root:admin /Library/NetBoot

Recursively, change permissions for owner & group to read, write & execute. And others to read & execute NetBoot directory & sub-directories...

chmod -R 775 /Library/NetBoot

Change Directory
Change directory to "/Library/NetBoot"...

cd /Library/NetBoot

Create Symlinks
Create symlink ".clients" from "NetBootClients0" in directory "/Library/NetBoot"
Using the "ln" utility with options "-s", "-h" & "-f" ...

ln -shf NetBootClients0 /Library/NetBoot/.clients

The "-s" option creates a symbolic links
The "-h" option will not follow the target file/directory if it is a symbolic links
The "-f" will unlink the target file if the symbolic link exists.

Create symlink ".sharepoint" from "NetBootSP0" in directory "/Library/NetBoot"

ln -shf NetBootSP0 /Library/NetBoot/.sharepoint

Configure NFS Exports

Create NFS Export for "NetBootSP0"...

echo "/Library/NetBoot/NetBootSP0 -ro -maproot=root" > /etc/exports

"-ro" option shares the directory read only
"-maproot=root" maps root account to remote system root account

I/O Redirection ">", redirect stdout to a file.
Creates the file if not present, otherwise overwrites it

Create NFS Export for "NetBootClients0"...

echo "/Library/NetBoot/NetBootClients0 -ro -maproot=root" >> /etc/exports

I/O Redirection ">>", redirects stdout to a file.
Creates the file if not present, otherwise appends to it.

Configure TFTP Service

Create Directories & Symlinks
Create NetBoot directory in "/private/tftpboot" directory...

mkdir /private/tftpboot/NetBoot

Created symlink "NetBootSP0" from "/Library/NetBoot/NetBootSP0" in directory "/private/tftpboot"...

ln -shf /Library/NetBoot/NetBootSP0 /private/tftpboot/NetBoot/NetBootSP0

Modify default tftp.plist
/System/Library/LaunchDaemons/tftp.plist

<?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>Disabled</key>
    <true/>
    <key>InitGroups</key>
    <true/>
    <key>Label</key>
    <string>com.apple.tftpd</string>
    <key>ProgramArguments</key>
    <array>
        <string>/private/tftpboot/deploystudiopc/bin/tftpd+</string>
        <string>-s</string>
        <string>/private/tftpboot</string>
    </array>
    <key>Sockets</key>
    <dict>
        <key>Listeners</key>
        <dict>
            <key>SockServiceName</key>
            <string>tftp</string>
            <key>SockType</key>
        <string>dgram</string>
        </dict>
    </dict>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <true/>
    </dict>
</dict>
</plist>

The "-s" option enables tftpd to chroot to the /tftpboot directory restricting access to outside files/directories. This is the default setup for the plist on Mac OS X 10.6.x client.

The "-i" option enables insecure mode which allows access to files outside the directory

/private/tftpboot directory

This is necessary for NetBoot to allow access to the images located in directory...

/Library/NetBoot/NetBootSP0

Edit tftp.plist
So, next you want change the "-s" option to "-i".To modify it you can use the following command...

sed -i '.bak' "s%-s%-i%g" "/System/Library/LaunchDaemons/tftp.plist"

Find string "-s" and replace with string "-i"
The sed "-i" option creates a backup with the extension .bak

Reload TFTP Service
Use launchctl to unload the tftp service

launchctl unload -w "/System/Library/LaunchDaemons/tftp.plist"

"-w" option overrides the "Disabled" key and sets it to false.

Use launchctl to load the tftp service

launchctl load -w "/System/Library/LaunchDaemons/tftp.plist"

Download NetBoot Image

To download the image from another client you could simple use Finder.

Or using command line...

Make directory for sharepoint mount path...

mkdir /Volumes/images

Mount AFP sharepoin...

mount_afp afp://[USERNAME]:[PASSWORD]@images.server.edu/path/to/image

Copy NetBoot image to directory /Library/NetBoot/NetBootSP0

cp -R /Volumes/images/[NETBOOT_IMAGE_NAME].nbi /Library/NetBoot/NetBootSP0

Unmount file system

umount /Volumes/images

Or if you update image routinely, it might be more efficent using rsync instead...

Copy NetBoot image to directory /Library/NetBoot/NetBootSP0

rsync -acv --progress --delete --ignore-existing [Source] [Target]

-a archive mode, same as -rlptgoD (no -H)
        -r recurse into directories
        -l copy symlinks as symlinks
        -p preserve permissions
        -t preserve times
        -g preserve group
        -o preserve owner
        -D preserve device & special files
-c skip based on checksum, not mod-time & size
-v increase verbosity
--progress: show progress during transfer
--delete delete extraneous files from dest dirs
--ignore-existing skip updating files that exist on receiver

rsync on Mac OS X 10.6.x

Default version of rsync installed on Mac OS X 10.6.x is 2.6.9 which is pretty outdated.

# rsync --version
rsync version 2.6.9 protocol version 29

Pre-Built Binaries for Mac OS X can be downloaded from "The MacPlace"

Or you can compile your own binary, see this web article called "Compiling Rsync with Resource Fork Support".

BOOTP Setup

Mac OS X 10.6.x client doesn't include /etc/bootpd.plist. So, I used the Mac OS X 10.6.x server to create a template that can be used on the client.

Here is an example of the template...

<?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>NetBoot</key>
    <dict/>
    <key>Subnets</key>
    <array>
          <dict>
                <key>allocate</key>
                <false/>
                <key>dhcp_domain_name</key>
                <string>DHCP_DOMAIN_NAME</string>
                <key>dhcp_domain_name_server</key>
                <array>
                     <string>DHCP_DOMAIN_NAME_SERVER_0</string>
                     <string>DHCP_DOMAIN_NAME_SERVER_1</string>
                </array>
                <key>dhcp_domain_search</key>
                <array>
                     <string>DHCP_DOMAIN_SEARCH</string>
                </array>
                <key>dhcp_router</key>
                <string>DHCP_ROUTER</string>
                <key>lease_max</key>
                <integer>3600</integer>
                <key>name</key>
                <string>NAME</string>
                <key>net_address</key>
                <string>NET_ADDRESS</string>
                <key>net_mask</key>
                <string>NET_MASK</string>
                <key>net_range</key>
                <array>
                     <string>NET_RANGE_START</string>
                     <string>NET_RANGE_END</string>
                </array>
                <key>selected_port_name</key>
                <string>en0</string>
                <key>uuid</key>
                <string>UUID</string>
          </dict>
    </array>
    <key>allow</key>
    <array/>
    <key>bootp_enabled</key>
    <false/>
    <key>deny</key>
    <array/>
    <key>detect_other_dhcp_server</key>
    <false/>
    <key>dhcp_enabled</key>
    <false/>
    <key>netboot_enabled</key>
    <array>
          <string>en0</string>
    </array>
    <key>old_netboot_enabled</key>
    <false/>
    <key>relay_enabled</key>
    <false/>
    <key>relay_ip_list</key>
    <array/>
    <key>startTime</key>
    <string>START_TIME</string>
</dict>
</plist>

Populate bootp.plist with Template Data
Use cat and a here file to create /etc/bootpd.plist with template data.

cat <<'EOF'
<?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>
.
.
.
</dict>
</plist>
EOF
) > /etc/bootp.plist

Modifying BOOTP setup for Domain name
Get Host Domain Name

DHCP_DOMAIN_NAME=`hostname | awk -F "." '{print $2"."$3"."$4}'`

Set "Find" & "Replace" variables

FIND="<string>DHCP_DOMAIN_NAME</string>"
REPLACE="<string>$DHCP_DOMAIN_NAME</string>"

Replace "Find" with "Replace" variable

sed -i '.bak' "s%$FIND%$REPLACE%g" /etc/bootpd.plist

Since there are many variables to modify in the bootpd.plist using the same basic process listed above, see the script available on this web page for details.

File Sharing Setup - Apple Filing Protocol

Next, depending on what you will be doing with NetBoot, like repairing clients with Disk Utility or DiskWarrior or if you are going to use Diskless NetBoot, you may or may not need to setup File Sharing with Apple Filing Protocol [AFP].

  • It is Needed for NetBoot network shadow files.
  • Mac OS X 10.6 Client limited to 10 Concurrent AFP connections
  • Used fseventer to reverse engineer client File Sharing setup.

fseventer window

dscl -read
Noticed file system changes to path...

/private/var/db/dslocal/nodes/Default/config/SharePoints/NetBootClients0.plist

To get the values you can turn on File Sharing using GUI "System Preferences -> Sharing"
Then use Directory Service command line utility [dscl] to read the sharepoints values needed.

For example..

# dscl . -read SharePoints/NetBootClient0
dsAttrTypeNative:afp_guestaccess: 1
dsAttrTypeNative:afp_name: NetBootClient0
dsAttrTypeNative:afp_shared: 1
dsAttrTypeNative:directory_path: /Library/NetBoot/NetBootClient0
dsAttrTypeNative:ftp_name: NetBootClient0
dsAttrTypeNative:sharepoint_group
_id: DD8A7526-901D-4D31-8B90-8EC8B71A1B43
dsAttrTypeNative:smb_createmask: 644
dsAttrTypeNative:smb_directorymask: 755
dsAttrTypeNative:smb_guestaccess: 1
dsAttrTypeNative:smb_name: NetBootClient0
dsAttrTypeNative:smb_shared: 1
AppleMetaNodeLocation: /Local/Default
RecordName: NetBootClient0
RecordType: dsRecTypeStandard:SharePoints

dscl -create
Create items needed for NetBoot sharepoint /Library/NetBoot/NetBootClients0

dscl . -create SharePoints/NetBootClients0
dscl . -create SharePoints/NetBootClients0 afp_guestaccess 1
dscl . -create SharePoints/NetBootClients0 afp_name NetBootClients0
dscl . -create SharePoints/NetBootClients0 afp_shared 1
dscl . -create SharePoints/NetBootClients0 directory_path /Library/NetBoot/NetBootClients0
dscl . -create SharePoints/NetBootClients0 ftp_name NetBootClients0

Not sure how to generate the sharepoint group id and works without specifying it.

dscl . -create SharePoints/NetBootClients0 sharepoint_group_id F7D1C81A-53EF-48E7-BF47-D2ABEF4F400B

 

dscl . -create SharePoints/NetBootClients0
dscl . -create SharePoints/NetBootClients0 afp_guestaccess 1
dscl . -create SharePoints/NetBootClients0 afp_name NetBootClients0
dscl . -create SharePoints/NetBootClients0 afp_shared 1
dscl . -create SharePoints/NetBootClients0 directory_path /Library/NetBoot/NetBootClients0
dscl . -create SharePoints/NetBootClients0 ftp_name NetBootClients0

Restart Services
Restarting DirectoryService after modifying database

killall DirectoryService

Make sure AFP is running by starting the daemon

/usr/sbin/AppleFileServer

Unload the AppleFileServer launch daemon after modifying database

launchctl unload -w /System/Library/LaunchDaemons/com.apple.AppleFileServer.plist

Loop Until Apple File Sharing enabled and gives error its already in use and sleep 30 seconds in-between loops

LOAD_APPLEFILESERVER_ERROR=`cat /tmp/load_applefileserver_error.txt | grep "Address already in use" | sed "s%bind(): %%g"`

INDEX=1

while [[ "${LOAD_APPLEFILESERVER_ERROR}" != "Address already in use" && "${INDEX}" -lt 10 ]]
do

launchctl load -w /System/Library/LaunchDaemons/com.apple.AppleFileServer.plist 2>/tmp/load_applefileserver_error.txt LOAD_APPLEFILESERVER_ERROR=`cat /tmp/load_applefileserver_error.txt | grep "Address already in use" | sed "s%bind(): %%g"`

let INDEX=INDEX+1

sleep 30

done