Add daemon account on OS X
I'm trying to manually install a daemon (Oracle Grid Engine) on my machine, and I would like it to run under an isolated account. What is the preferred way, using Directory Services, to add a "system" account to the local machine on OS X? Plenty of them exist in /etc/passwd (_www
, _dovecot
, etc.), but comments at the top of that file say that it isn't used except in single-user mode.
I'm running on 10.6, and do not require any special networked account management. I'm hoping for something simple--the equivalent of useradd
on nearly every other Unix-like OS.
dscl is the command you are looking for.
I tried the script from par, and found a few issues. So I modified it for one specific userid and for OS X Mavericks (10.9).
I found that there was a couple extraneous records added to the User account under Mavericks -- a PasswordPolicyOptions and an AuthenticationAuthority record -- that needed to be removed to correctly mimic other builtin service user accounts (like _www).
I also added the Password and RealName records to the Group account.
I created a custom, one off, script just for a WSGI service account. Here's the updated script.
#! /bin/bash
#
# Check that we are superuser (i.e. $(id -u) is zero)
if (( $(id -u) ))
then
echo "This script needs to run as root"
exit 1
fi
username_=wsgi
uid_=240
realname_="WSGI Daemon"
dscl . -create /Groups/_$username_
dscl . -create /Groups/_$username_ PrimaryGroupID $uid_
dscl . -create /Groups/_$username_ RecordName _$username_ $username_
dscl . -create /Groups/_$username_ RealName $realname_
dscl . -create /Groups/_$username_ Password \*
dscl . -create /Users/_$username_
dscl . -create /Users/_$username_ NFSHomeDirectory /xpt/local/apache2/wsgi/api
dscl . -create /Users/_$username_ Password \*
dscl . -create /Users/_$username_ PrimaryGroupID $uid_
dscl . -create /Users/_$username_ RealName $realname_
dscl . -create /Users/_$username_ RecordName _$username_ $username_
dscl . -create /Users/_$username_ UniqueID $uid_
dscl . -create /Users/_$username_ UserShell /usr/bin/false
dscl . -delete /Users/_$username_ PasswordPolicyOptions
dscl . -delete /Users/_$username_ AuthenticationAuthority
Note that after running this script the /etc/passwd and /etc/groups files are not updated. I believe they are updated on reboot.
EDIT: Updated Jan 9, 2014 for OS X Mavericks (suggestions from Dave, thanks!)
I wrote a bash script to do this. It will use the first unused uid which is less than or equal to 500 (daemon account uids on Mac OS X) that also has an identical unused gid.
Save the script to a file named add_system_user.sh
and set it executable with chmod 755 add_system_user.sh
.
Then let's say you want to add a daemon/system user called par. You would run this script like so:
sudo add_system_user.sh par
And you will get a system user called _par
which is aliased to par
(the name you requested) and has a matching uid and gid (e.g. 499 or whatever it found).
Here's the script:
#!/bin/bash
if (( $(id -u) )) ; then
echo "This script needs to run as root"
exit 1
fi
if [[ -z "$1" ]] ; then
echo "Usage: $(basename $0) [username] [realname (optional)]"
exit 1
fi
username=$1
realname="${2:-$username}"
echo "Adding daemon user $username with real name \"$realname\""
for (( uid = 500;; --uid )) ; do
if ! id -u $uid &>/dev/null; then
if ! dscl /Local/Default -ls Groups gid | grep -q [^0-9]$uid\$ ; then
dscl /Local/Default -create Groups/_$username
dscl /Local/Default -create Groups/_$username Password \*
dscl /Local/Default -create Groups/_$username PrimaryGroupID $uid
dscl /Local/Default -create Groups/_$username RealName "$realname"
dscl /Local/Default -create Groups/_$username RecordName _$username $username
dscl /Local/Default -create Users/_$username
dscl /Local/Default -create Users/_$username NFSHomeDirectory /var/empty
dscl /Local/Default -create Users/_$username Password \*
dscl /Local/Default -create Users/_$username PrimaryGroupID $uid
dscl /Local/Default -create Users/_$username RealName "$realname"
dscl /Local/Default -create Users/_$username RecordName _$username $username
dscl /Local/Default -create Users/_$username UniqueID $uid
dscl /Local/Default -create Users/_$username UserShell /usr/bin/false
dscl /Local/Default -delete /Users/_$username AuthenticationAuthority
dscl /Local/Default -delete /Users/_$username PasswordPolicyOptions
break
fi
fi
done
echo -e "Created system user $username (uid/gid $uid):\n"
dscl /Local/Default -read Users/_$username
echo -e "\nYou can undo the creation of this user by issuing the following commands:\n"
echo "sudo dscl /Local/Default -delete Users/_$username"
echo "sudo dscl /Local/Default -delete Groups/_$username"
Here's an article that explains how to use dscl to create a user account.
osxdaily.com article
Here's a version of Dave's script, which also checks if the user/group exists before creating it:
#!/bin/sh # creates service account user similar to Linux adduser command # to view existing users and ids try: # dscl . -readall /Users UniqueID | sort -nk 2 die () { echo >&2 "$@" exit 1 } echo "Usage: sudo $0 username uid realname" echo "NOTES: username shouldn't start with the underscore (it will be appended by the script)" echo " check that the user does not exist and get the free ID number in 1000 range" echo " e.g. with dscl . -readall /Users UniqueID | sort -nk 2" echo "" # Check that we are superuser (i.e. $(id -u) is zero) [ `id -u` -eq 0 ] || die "This script needs to run as root" [ "$#" -eq 3 ] || die "Error: 3 arguments required: username, uid and realname" username_=$1 uid_=$2 realname_=$3 nfs_homedir="/var/tmp" user_shell="/usr/bin/false" echo "Checking if the user/group exists: \c" check_uuid=`dscl . -search /Users UniqueID $uid_` check_upgid=`dscl . -search /Users PrimaryGroupID $uid_` check_urn=`dscl . -search /Users RecordName _$username_` check_grn=`dscl . -search /Groups RecordName _$username_` [ ${#check_uuid} = 0 ] || die "failed!\nERROR: Non-unique User UniqueID:\n\n`dscl . -read /Users/_$username_ RecordName PrimaryGroupID RealName` \n\nTo view existing users/ids run: dscl . -readall /Users UniqueID | sort -nk 2" [ ${#check_upgid} = 0 ] || die "failed!\nERROR: Non-unique User PrimaryGroupID\n\n`dscl . -read /Users/_$username_ RecordName PrimaryGroupID RealName` \n\nTo view existing users/ids run: dscl . -readall /Users UniqueID | sort -nk 2" [ ${#check_urn} = 0 ] || die "failed!\nERROR: Non-unique User RecordName\n\n`dscl . -read /Users/_$username_ RecordName PrimaryGroupID RealName` \n\nTo view existing users/ids run: dscl . -readall /Users UniqueID | sort -nk 2" [ ${#check_grn} = 0 ] || die "failed!\nERROR: Non-unique Group RecordName\n\n`dscl . -read /Groups/_$username_ RecordName PrimaryGroupID RealName` \n\nTo view existing users/ids run: dscl . -readall /Users UniqueID | sort -nk 2" echo "we're good to go!" # echo "Continue (y/n) ? " # read input_ # [ "$input_" = "y" ] || die "as you wish..." echo "Creating User: \c" dscl . -create /Groups/_$username_ dscl . -create /Groups/_$username_ PrimaryGroupID $uid_ dscl . -create /Groups/_$username_ RecordName _$username_ $username_ dscl . -create /Groups/_$username_ RealName "$realname_" dscl . -create /Groups/_$username_ Password \* dscl . -create /Users/_$username_ dscl . -create /Users/_$username_ NFSHomeDirectory $nfs_homedir dscl . -create /Users/_$username_ Password \* dscl . -create /Users/_$username_ PrimaryGroupID $uid_ dscl . -create /Users/_$username_ RealName "$realname_" dscl . -create /Users/_$username_ RecordName _$username_ $username_ dscl . -create /Users/_$username_ UniqueID $uid_ dscl . -create /Users/_$username_ UserShell $user_shell dscl . -delete /Users/_$username_ PasswordPolicyOptions dscl . -delete /Users/_$username_ AuthenticationAuthority echo "done!"
and a script to delete user:
#!/bin/sh # delete service user similar to Linux userdel command, but leaving the files intact # to view existing users and ids try: # dscl . -readall /Users UniqueID | sort -nk 2 die () { echo >&2 "$@" exit 1 } # Check that we are superuser (i.e. $(id -u) is zero) [ `id -u` -eq 0 ] || die "This script needs to run as root" [ "$#" -eq 1 ] || die "Error: username arguments is required!" username_=$1 dscl . -delete /Users/$username_ dscl . -delete /Groups/$username_ echo "done!"