OSU Navigation Bar

Mac OS X BSDP Service using a GNU/Linux Server

Home > Linux > How-tos > Mac OS X BSDP Service using a GNU/Linux Server

 

Author: Jeff McCune

Avoid paying for a Mac OS X Server license if just need to automate your host installation work flow.  Installing machines by hand is such a PITA...

Note: Even seasoned Unix administrators will spend a few hours setting all this crap up.  Depending on your salary, it may be wise to just throw down for a Mac OS X Server license.  Apple really does just let you click it in...

With that said, this information will give you unparalleled control over the whole system.

NOTE: HTTP is easier than NFS to get working with both Intel and PowerPC as the service providing the root file system image.  NFS works, I'd just recommend starting with HTTP since it's a relatively simple and well understood protocol.

References

Assumptions

This is a private network, so you should have total control.  Here's what works for me.

ServiceWhy we need it
NetRestore Helper NetBoot-Install Images are easeily created using this free tool written by Mike Bombich.
CentOS CentOS is like RedHat Enterprise, just with Yum preconfigured.  We'll use CentOS to host the services required to make BSDP NetBoot-Install work.
ISC DHCP server Service BSDP Broadcast Requests on the private network.  ISC DHCP now works fine with Intel and PowerPC machines without patches to it's sourcecode.
TFTP server We need to serve the darwin kernel, bootloader, and kernel extension cache file.  A Mac's firmware speaks TFTP initially.
DNS Server Service DNS Requests on the private network.  We'll use dnsmasq since it's trivial to setup.
NFS Server Serve Root File System.  I prefer NFS to HTTP since the clients can re-mount the NFS server easily.  NetRestore Helper exposes this feature as a single checkbox.
HTTP Server Serve Root File System DMG's & Rails Application Server. It's generally good to have an HTTP server handy, since it's easy to understand and configure.  I'm partial to Lighttpd since it's small, fast, and easy to setup.

Network Configuration

We're assuming the linux gateway dual-homed server is located at:

Internal Interface 
Interface: eth1
IP: 192.168.7.1
NetMask: 255.255.255.0

 

External Interface 
Interface: eth0
IP: DHCP

I use this bash script to route traffic from DHCP clients on the private network to the external network, using the linux server as a router gateway:

#!/bin/bash
#
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth0 -m state --state NEW,INVALID -j DROP
echo 1 > /proc/sys/net/ipv4/ip_forward

 

Installing Software

ISC DHCP

Install ISC-DHCP from the CentOS base repository:

yum -y install dhcp

Lighttpd

Install HTTP Server:

yum -y install lighttpd

TFTP Server

Install a TFTP server:

yum -y install tftp-server

DNS Server

Install a lightweight DNS server:

yum -y install dnsmasq

Configuring the System

ISC-DHCP Server BSDP Configuration

Here's how we configure ISC DHCP to service Apple BSDP requests. This should work with BSDP requests coming from OpenFirmware and Intel EFI.

First, tell dhcpd to start only on the internal interface. We don't want to hijack an existing DHCP server on the public LAN.

# /etc/sysconfig/dhcpd
DHCPDARGS=eth1

Enable DHCP startup on boot:

chkconfig --level 2345 dhcpd on

NOTE: Many thanks to **Christopher J. Suleski** for emailing me a working ISC DHCP configuration for Intel machines. Without his efforts, this information wouldn't be available. Sample /etc/dhcpd.conf:

# JJM ISC DHCP Configuration, providing BSDP Service to Apple Hardware.
# 2006-12-05
#
ddns-update-style none;
ddns-updates off;
ignore client-updates;
allow booting;
authoritative;

class "AppleNBI-i386" {
  match if substring (option vendor-class-identifier, 0, 14) = "AAPLBSDPC/i386";
  option dhcp-parameter-request-list 1,3,17,43,60;
  if (option dhcp-message-type = 1) { option vendor-class-identifier "AAPLBSDPC/i386"; }
  if (option dhcp-message-type = 1) { option vendor-encapsulated-options 08:04:81:00:00:67; }
  # The Apple Boot Loader binary image.  This file will in turn TFTP the kernel image and extension cache.
  filename "macnbi-i386/booter";

  ## JJM Root FS DMG on HTTP Server:
  # option root-path "http://192.168.7.1/Netboot/NetBootSP0/MacOSX10.4.5.i386.JJM.nbi/NetInstall-Restore.dmg";

  ## JJm Root FS DMG on NFS Server.
  # (Note the placement of the second ":"  This indicates where /var/netboot is mounted on each client.
  # The Resources folder should be in the folder indicated by the :, /nbi in this instance.

  ## NOTE: THIS ROOT PATH IS TOO LONG AND WILL NOT WORK.
  # option root-path "nfs:192.168.7.1:/disk/0/Netboot/NetBootSP0:MacOSX10.4.5.i386.JJM.nbi/NetInstall-Restore.dmg";

  ## NOTE: Try to keep the root path as short as possible.  I copy the DMG files to /nbi and export that folder.
  option root-path "nfs:192.168.7.1:/nbi:NBI-i386.dmg";
}

class "AppleNBI-ppc" {
  match if substring (option vendor-class-identifier, 0, 13) = "AAPLBSDPC/ppc";
  option dhcp-parameter-request-list 1,3,6,12,15,17,43,53,54,60;
  # The Apple Boot Loader binary image.  This file will in turn TFTP the kernel image and extension cache.
  filename "macnbi-ppc/booter";
  option vendor-class-identifier "AAPLBSDPC";

  if (option dhcp-message-type = 1) {
    option vendor-encapsulated-options 08:04:81:00:00:09;
  }
  elsif (option dhcp-message-type = 8) {
    option vendor-encapsulated-options 01:01:02:08:04:81:00:00:09;
  }
  else {
    option vendor-encapsulated-options 00:01:02:03:04:05:06:07;
  }

  ## JJM Root FS DMG on HTTP Server:
  # option root-path "http://192.168.7.1/Netboot/NetBootSP0/MacOSX10.4.5.powerpc.JJM.nbi/NetInstall-Restore.dmg";
  ## JJm Root FS DMG on NFS Server.
  # (Note the placement of the second ":"  This indicates where /var/netboot is mounted on each client.
  # The Resources folder should be in the folder indicated by the :, /nbi in this instance.

  ## NOTE: THIS ROOT PATH IS TOO LONG AND WILL NOT WORK.
  # option root-path "nfs:192.168.7.1:/disk/0/Netboot/NetBootSP0:MacOSX10.4.5.powerpc.JJM.nbi/NetInstall-Restore.dmg";

  ## NOTE: Try to keep the root path as short as possible.  I copy the DMG files to /nbi and export that folder.
  option root-path "nfs:192.168.7.1:/nbi:NBI-ppc.dmg";
}

subnet 192.168.7.0 netmask 255.255.255.0 {
  pool {
    range 192.168.7.100 192.168.7.199;
  }

  default-lease-time 7200; # 2 hours
  max-lease-time 86400; # 1 day

  option domain-name "alpha.secure.lan";
  option routers 192.168.7.1;
  option subnet-mask 255.255.255.0;
  option broadcast-address 192.168.7.255;
  option domain-name-servers 192.168.7.1;
  option time-offset -18000; # EST
  allow unknown-clients;
}

Start the DHCP server:

/etc/init.d/dhcpd stop
/etc/init.d/dhcpd start

TFTP Server Configuration

We need this to serve the kernel image to the client firmware. Enable TFTP Services:

chkconfig --level 2345 tftp on

Copy the booter into place. Obtain the bootloader and kernel files from the NetBoot-Install image you created with NetRestore Helper, or by generating them yourself (See the Extras Section. My /tftpboot looks like this:

[root@tom ~]# find /tftpboot/ -type f
/tftpboot/macnbi-i386/mach.macosx
/tftpboot/macnbi-i386/booter
/tftpboot/macnbi-i386/mach.macosx.mkext
/tftpboot/macnbi-ppc/mach.macosx
/tftpboot/macnbi-ppc/booter
/tftpboot/macnbi-ppc/mach.macosx.mkext

Restart the TFTP server:

/etc/init.d/xinetd restart

You can test TFTP transfers using a tftp client:

yum install tftp
tftp localhost
binary
get /macnbi-ppc/booter

Lighttpd NetBoot Configuration

We need an HTTP server to provide our root file system over the network. It will also server the image we clone to the client hard disk.

# /etc/lighttpd/lighttpd.conf
# Add / Uncomment

server.modules = ( "mod_alias", "mod_access", "mod_accesslog" )
alias.url = ( "/Netboot/" => "/disk/0/NetBoot/" )
server.dir-listing = "enable"

The "*.nbi" folders created with Netrestore Helper should be copied to /disk/0/NetBoot/NetBootSP0/

Enable lighttpd startup:

chkconfig --level 2345 lighttpd on

Restart lighttpd:

/etc/init.d/lighttpd restart

NFS Server

NOTE: NFS works with an unpatched ISC DHCP server, but you must keep the root-path option string as short as possible. There's an option length limit. I'm not sure of the exact value, but it's enough to be annoying. This is why the ISC patch is necessary in some cases, which you may have seen floating around the web.

I prefer NFS over HTTP, since it allows the client to easily mount the Resources directory of the NetBoot server. This emulates the behavior of a standard Mac OS X server, and the option to mount the server's file system is a single checkbox in NetRestore Helper.

My /etc/exports looks like this:

# NFS Export the NetBoot Folder
/disk/0/NetBoot   192.168.7.0/24(async,ro,no_root_squash,insecure)
# Shorten the path string we need to send over DHCP.
/nbi              192.168.7.0/24(async,ro,no_root_squash,insecure)

Restart the NFS Services:

/etc/init.d/nfs restart
/etc/init.d/nfslock restart

Configure NFS to start on boot:

chkconfig --level 2345 nfs on
chkconfig --level 2345 nfslock on

In order to use NFS between the NBI clients and server, you must modify the DHCP configuration to inform the clients NFS is used instead of HTTP.

Modify the root-path class option to use NFS instead of HTTP:

# /etc/dhcpd.conf

# Example for PowerPC:
option root-path "nfs:192.168.1.7:/nbi:NBI-ppc.dmg";

# Example for i386:
option root-path "nfs:192.168.1.7:/nbi:NBI-i386.dmg";

Important: Note the placement of the second ":" in the root-path option for NFS. This indicates what is mounted onto /var/netboot on each client. This helps us access the Resources folder on each client. I strongly recommend keeping data inside the Resources folder as a working copy of some revision control system... CVS, Subversion, Bazaar, etc...

Test NetBoot

At this point, all the services required to boot a Mac OS X workstation from the network are installed and configured.

  1. Plug the mac into the private network switch.
  2. Power it on and hold the Option key.
  3. Press N to search the network for a netboot server.
  4. Monitor the server logs with tail -n 100 -f /var/log/messages
  5. Try and boot from the network.
  6. Bring up a terminal Command T once NetRestore Helper starts up.

dnsmasq configuration - DNS Service

We need to turn off DHCP support in dnsmasq:

/etc/dnsmasq.conf:

local=/lan/
read-ethers
listen-address=192.168.7.1,127.0.0.1
no-dhcp-interface=eth1
expand-hosts

/etc/ethers

Generate host entries for DNS. These hosts will look like dhcp102.alpha.secure.lan:

perl -e 'for ($x=100;$x<200;$x++) { printf "192.168.7.%02d\t dhcp%03d\n", $x, $x; }' \
>> /etc/hosts

Testing Forward DNS:

dig dhcp107 @127.0.0.1

You should get back:

; <<>> DiG 9.2.4 <<>> dhcp107 @127.0.0.1
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19499
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;dhcp107.                       IN      A

;; ANSWER SECTION:
dhcp107.                0       IN      A       192.168.7.107

;; Query time: 7 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Dec  5 21:08:13 2006
;; MSG SIZE  rcvd: 41

Testing Reverse DNS: dig -x 192.168.1.107 @127.0.0.1 You should get back:

; <<>> DiG 9.2.4 <<>> -x 192.168.7.102 @127.0.0.1
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38306
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;102.7.168.192.in-addr.arpa.    IN      PTR

;; ANSWER SECTION:
102.7.168.192.in-addr.arpa. 0   IN      PTR     dhcp102.alpha.secure.lan.

;; Query time: 6 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Dec  5 21:06:17 2006
;; MSG SIZE  rcvd: 82

Extras

SSH Keys

It's a good idea to have all the hosts agree on their SSH host identification keys:

Create directories for every host:

perl -e 'for ($x=100;$x<200;$x++) { printf "mkdir dhcp%03d\n", $x; }' | bash

Generate SSH Keys for every host:

perl -e 'for ($x=100;$x<200;$x++) { printf "ssh-keygen -q -t rsa -f dhcp%03d/ssh_host_rsa_key -N \"\" -C \"\"\n", $x; }' | bash
perl -e 'for ($x=100;$x<200;$x++) { printf "ssh-keygen -q -t dsa -f dhcp%03d/ssh_host_dsa_key -N \"\" -C \"\"\n", $x; }' | bash

HOWTO: Update kernel executable and drivers

Sometimes it's beneficial to update to a more recent version of the Darwin kernel and extension cache in order to boot new hardware without generating an entirely new NetBoot-Install image using NetRestore Helper.

Here's how.  We'll generate the files from a machine of each architecure and copy them into the /tftpboot folder of the Linux BSDP server.

Intel Machine

These steps must be run on an Intel Mac.  In 10.5, Leopard, we'll finally have a unified system.

Make the Container:
  mkdir /tmp/macnbi-i386

Generate the kernel image:
  ditto /mach_kernel /tmp/macnbi-i386/mach.macosx

Or, thin out the fat kernel binary:
  lipo -extract i386 -output /tmp/macnbi-i386/mach.macosx /mach_kernel

Create the kernel extension cache:
  kextcache -a i386 -s -l -n -z -m /tmp/macnbi-i386/mach.macosx.mkext /System/Library/Extensions

Copy the Boot Loader binary:
  ditto /usr/standalone/i386/boot.efi /tmp/macnbi-i386/booter

You'll then want to copy /tmp/macnbi-i386 to the tftpboot folder on the Linux BSDP server and make sure /etc/dhcpd.conf is providing the correct path to the booter file.

PowerPC Machine

These steps must be run on an PowerPC Mac.  In 10.5, Leopard, we'll finally have a unified system.

Make the Container:
  mkdir /tmp/macnbi-ppc

Copy the Mach Kernel:
  ditto /mach_kernel /tmp/macnbi-ppc/mach.macosx

Create the kernel extension cache:
  kextcache -a ppc -s -l -n -z -m /tmp/macnbi-ppc/mach.macosx.mkext /System/Library/Extensions

Copy the Boot Loader binary:
  ditto /usr/standalone/ppc/bootx.bootinfo /tmp/macnbi-ppc/booter

You'll then want to copy /tmp/macnbi-ppc to the tftpboot folder on the Linux BSDP server and make sure /etc/dhcpd.conf is providing the correct path to the booter file.