Workgroup Management without a server

From Provider Wiki

Jump to: navigation, search


Contents

Workgroup Management without a server

Apple provides some wonderful tools with MacOS X Server to put restrictions and preferences onto groups of computers. When you have OpenDirectory setup so that the computers connect to the server they will get these settings live, but if for some reason you do not have MacOS X Server running you have no official way of managing the computers.

But there is still a way of doing this, and that is what this article will walk you through using a local NetInfo group to manage users on individual computers. Note that this will only really work for 10.4.3 and later, since we are using nested groups to get this done. This could be re-worked to apply more generally, but that is for another article.

There are three parts of this process:

  • Setting the management settings locally on one computer
  • Freeze-drying these settings
  • Applying these settings to other computers (one-by-one)

Create the Workgroup Settings

Getting into Workgroup Manager

While MacOS X Server is a pay-for product, you can download the Server Admin tools for free from Apple. You will need these tools in order to setup the Workgroup settings on your computer. The latest version can be found by going to http://www.apple.com/support/downloads/ and searching for "Server Admin Tools".

Once you have the Admin Tools installed launch the "Workgroup Manager" application. When it asks you where to connect to fill in "localhost" as the address, and then the login name and password of a local admin. This cannot be a "directory admin" because you will be connecting to the local NetInfo database (which does not recognize directory admins).

Once you have logged in a screen should come up and there should be a message towards the upper left of the window, something like "Authenticated as <username> to local directory: /NetInfo/DefaultsLocalNode". If not, then you need to click on the small world icon and switch it to "Local".

Setting up a group for management

You should be in the "Accounts" section, and in the lower-left-hand pane go to the middle tab (looks like three people), this will list all of the groups that are on this computer (minus the "non-user" ones). For this purpose we are going to create a new group, so go up to the toolbar and click on "New Group". For the purposes of this demo we will name this group "Demo Group" and give it the "short name" of "demogroup". The default Group ID should be in the 1020's, and unless you know that this is going to conflict with groups on your other computers, you do not need to change it.

Now that we have a group, we need to add users to it to control. In the "Members" section click on the "+" symbol. This will show the users/groups drawer. Here is where you will be selecting a group of users from groups you already have. If you are using a directory service (Apple's Open Directory, Microsoft's ActiveDirectory, or any other), then you need to make sure that it is included in the list. One way is to use the little globe icon in this drawer to "Search Path", or to the specific service (remembering that that service has to be available on the client computers). Go to the "groups" tab, and drag an existing group into the "Members" area, and hit save.

Now you can go into the "Preferences" section (available through the toolbar) and make any of the other changes you wish. Also pay attention to the "Group Folder" section, as with a little playing around this can be very helpful. Since this is all live on the computer you can simply log in as a user from that group and see that things have applied. You might want to leave a NetInfo window open and make sure to delete the "settings/mcx_cache" directory every time you make a change, as sometimes this will cause problems.

Setting up a computer for management

If you are going to create a script to manage the computer settings (apply to all users, and can include setting for the login window) then you need to use the computers section of the admin interface. So, rather than choosing the group of three people, you choose the tab with two overlapping boxes. You will probably want to add your own computer to the list you are going to manage (so you can test your settings) but the script will actually take care of all of that.

The script as presented here is hard-coded to always look for a group named "LocalComputer" and to create an identical group on the remote computer. Eventually I will get around to allowing for multiple groups on the template computer, but there should only be one group on the target computer, so I will probably leave the name on the target computers the same.

Packaging the settings

Now that you have settings made up for your group it is time to freeze-dry the settings. Normally this is where things would get very complicated, as it involves a few command-line utilities that you have to get just the right information, and this had to happen correctly on both the template computer and the target computers. But I have put together a pair of scripts that will make this relatively painless. One works for computer settings, the other group settings.

The groups script

This script should be run on the template computer from the command line. I have named it "freezeDriedMCX.pl", but there is no requirement that that name stay. To run the script just run it from the command line with a single argument, the "short" name of the group you are reading for transport.

freezeDriedMCX.pl:

#!/usr/bin/perl

# this script created a bash script with a freeze dried NetInfo group
#	when the resulting script is run on a computer it will inject that group into the comptuer
#	the idea here is to be able to insert MCX settings on a computer that is not bound to OD

if ($#ARGV == -1) {
	print STDERR "Usage: $0 <group name>\n";
	exit 1;
}

$groupName = $ARGV[0];
if (!`/usr/bin/nicl / -search /groups 1 1 name $groupName | /usr/bin/grep "^gid:" | /usr/bin/sed "s/gid: //"`) {
	print STDERR "There is no group '$groupName' in NetInfo on this computer.\n";
	exit 1;
}

$encodedData = `/usr/bin/nidump -r /groups/$groupName / | /usr/bin/openssl enc -base64`;
($groupID, ) = `/usr/bin/nicl / -read /groups/$groupName gid` =~ /^gid: (\d+)/;

$scriptText = <<EOF;
#!/bin/bash

# this script inserts (or overwrites) the '$groupName' group
#	this should be a group that has MCX settings applied to it
#	this script should be able to be pushed out via ARD

groupID=$groupID
groupName="$groupName"

if [ `/usr/bin/id -u` != 0 ]; then
        /bin/echo "this script must be run as root!";
        exit 1;
fi

# now to check if we would be overwiting a differnt group
localGroupFromGID=`/usr/bin/nicl / -search /groups 1 1 gid \$groupID | /usr/bin/grep "^name:" | /usr/bin/sed "s/name: //"`
localGroupFromName=`/usr/bin/nicl / -search /groups 1 1 name \$groupName | /usr/bin/grep "^gid:" | /usr/bin/sed "s/gid: //"`

if [ -n "\$localGroupFromGID" -a -n "\$localGroupFromName" ]; then
	if [ "\$localGroupFromGID" = "\$groupName" -a "\$localGroupFromName" = "\$groupID" ]; then
		# this is the same group, so we will update it
		action="update"
	else
		echo "The group with GID '\$groupID' does not have the name '\$groupName', so nothing was done"
		exit 2
	fi
else
	if [ -z "\$localGroupFromGID" -a -z "\$localGroupFromName" ]; then
		# there were no intersecting groups, so we can continue and create it
		action="create"
	else
		/bin/echo "There is a group with GID '\$groupID' or the name '\$groupName', but not both, so nothing was done"
		exit 2
	fi
fi

# here is the netinfo data, base64 encoded for transport
groupData='$encodedData'

# I should probably do some error checking here...
/usr/bin/nicl / -create /groups/$groupName
/bin/echo "\$groupData" | /usr/bin/openssl enc -base64 -d | /usr/bin/niload -r /groups/$groupName /
/usr/bin/nicl / -delete /config/mcx_cache 2>/dev/null
/usr/bin/nicl / -flush

EOF

$targetName = $groupName . "Installer.bash";
if (-e $targetName) {
	if (-f $targetName and -w $targetName) {
		# we can write to this file, so continue on
	} else {
		print STDERR "There is an unwritable file or a directory at $groupName";
		exit 1;
	}
} else {
	if (-w ".") {
		# we can write to the directory, so continue on
	} else {
		print STDERR "Cannot write a new file to this dirctory";
		exit 1;
	}
}

open(TARGETFILE, ">$targetName") || die "could not write to file";
print TARGETFILE $scriptText;
close(TARGETFILE);

exit 0;

The computers script

This script should be run on the template computer from the command line. I have named it "computerMCXWriter.pl", but there is no requirement that that name stay. To run the script just run it from the command line without any arguments. It will produce a file named "LocalComputerMCXInstaller.bash" that needs to be run on the target computers.

computerMCXWriter.pl:

#!/usr/bin/perl

# this script will try and freeze dry a computers setting for a local computer
#	the bash script that it produces, when run will setup a computer_lists in the NetInfo
#	database on that computer, and add localhost to that list

if ($#ARGV == -1) {
	print STDERR "Usage: $0 <group name>\n";
	exit 1;
}

$inputName = $ARGV[0];
$listName = "LocalComputer";
if (!`/usr/bin/nicl / -search /computer_lists 1 1 name $inputName 2>/dev/null | /usr/bin/grep "^name:" | /usr/bin/sed "s/name: //"`) {
	if (length(@lists) > 0) {
		print STDERR "There is no computer list '$inputName' in NetInfo on this computer.\n";
		print STDERR "Here are the lists on this computer:\n";
		@lists = `/usr/bin/nicl / -list /computer_lists`;
		foreach $item (@lists) {
			($processedItem, ) = $item =~ /\d+\s+(\S+)/;
			print STDERR "\t" . $processedItem . "\n";
		}
	} else {
		print STDERR "There is no computer lists in NetInfo on this computer.\n";
	}
	
	exit 1;
}

# the sed in this next line allows for other groups to be chosen
$encodedComputerList = `/usr/bin/nidump -r /computer_lists/$inputName / | sed 's/"name" = ( "$inputName" )/"name" = ( "$listName" )/' | /usr/bin/openssl enc -base64`;
chomp($encodedComputerList);

$scriptText = <<EOF;
#!/bin/bash

# This script inserts a managed computer group into the local NetInfo database
#	it also creates a computer record with the local computer, and assigns it to this group

if [ `/usr/bin/id -u` != 0 ]; then
        /bin/echo "this script must be run as root!";
        exit 1;
fi

# first a few variables from the generator script
listName='$listName'

encodedComputerList='$encodedComputerList'

# this should get the proper MAC address
macAddress=`/sbin/ifconfig en0 | /usr/bin/grep 'ether' | /usr/bin/sed "s/^[[:space:]]ether //"`

# generate the /computers entry for this computer
#	note that this could change if the motherboard is replaced
#	also note that I am not makeing a GUID, it seems to work without
/usr/bin/nicl / -create /computers/localMCXComputer en_address \$macAddress
/usr/bin/nicl / -create /computers/localMCXComputer comment Auto-Created

# generate the /computer_lists entry
/bin/echo "\$encodedComputerList" | /usr/bin/openssl enc -base64 -d | /usr/bin/niload -r /computer_lists/$listName /

# flush out the MCX settings
if [ -f /System/Library/CoreServices/mcxd.app/Contents/Resources/MCXCacher ]; then 
	# this is a 10.4 system
	/bin/rm -r /Library/Managed\\ Preferences/* 2>/dev/null
	/System/Library/CoreServices/mcxd.app/Contents/Resources/MCXCacher -f 1>/dev/null
else
	# pre 10.4
	/usr/bin/nicl / -delete /mcx_cache 
fi

exit 0
EOF

$targetName = $inputName . "MCXInstaller.bash";
if (-e $targetName) {
	if (-f $targetName and -w $targetName) {
		# we can write to this file, so continue on
	} else {
		print STDERR "There is an unwritable file or a directory at $targetName";
		exit 1;
	}
} else {
	if (-w ".") {
		# we can write to the directory, so continue on
	} else {
		print STDERR "Cannot write a new file to this dirctory";
		exit 1;
	}
}

open(TARGETFILE, ">$targetName") || die "could not write to file";
print TARGETFILE $scriptText;
close(TARGETFILE);

exit 0;

Using the script

Note: I am assuming that you have made the script executable. If you have not, you simply have to add a "perl" call before the script name.

For the Groups version:

From the command line (in the directory with the script) you just have to type
freezeDriedMCX.pl <group_name>
where <group_name> is the short-name of the group you are working on. The script will then create a file in the directory you are working in with the name <group_name>Installer.bash. This resulting bash script is what you will need to run on the target computers to put the group on each of them.

Distributing the settings

Once you have the freeze-dried group then you need to apply it to all of the computers that you want to have this setting. If you are only working on this with a few computers you can do this by hand, or work it into a build process. But if you have more computers to deal with you probably have one of the cluster management systems. Personally I use AppleRemoteDesktop for this purpose, and this script was initially written to easily be distributable by the "send shell script" feature of that software.

But due to a bug in ARD 2 and 3 any script this large will cause ARD to hang on the client end, thus making it worthless. However, you can create an installer package that includes this script as a postflight script, and that will do the job for you. With a package maker such as Iceberg this is very easy.

Once you have the package made it is very easy to have ARD send it out to be installed on selected computers. Note that there are a few safety measures built into the script to keep it from overwriting things, and the package does have to have the "install as root" check-box checked. Otherwise the installer will fail when the script tells it to fail.