10

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.

5 Answers 5

3

dscl is the command you are looking for.

1
  • +1 because this is the definitive answer. If you want a GUI, you can download the Server Tools and point Workgroup Manager at your own machine to do essentially the same thing.. Sep 17, 2010 at 21:35
12

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.

1
  • 1
    Thanks Dave. I updated my script (below) to use your Mavericks discoveries and take an optional real-name parameter.
    – par
    Jan 10, 2014 at 5:40
12

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"
1
  • This worked well for me on 10.9. Thanks!
    – jbyler
    Aug 19, 2014 at 19:22
2

Here's an article that explains how to use dscl to create a user account.

osxdaily.com article

2
  • Thanks. The man pages tell you what commands you can use, but don't give you any help on what fields to use and what to set them to. I was just going to copy the config from another account, but this gives a good example.
    – Tim Yates
    Sep 17, 2010 at 23:12
  • For a hidden system account, take a look at some existing system accounts for better examples. E.g. dscl /Local/Default -read /Users/_sshd (dscl /Local/Default -ls /Users will get you a list of local accounts). Note that you don't have to set all attributes; GeneratedUID is generated randomly, and RecordType is automatic metadata. Also, most OS X system accounts have a primary account name that starts with underscore, and an alias without it for backward compatibility; I recommend using the underscore convention, but don't bother with the plain alias unless you need to. Sep 18, 2010 at 1:45
2

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!"
1
  • You're a magician, thanks a lot! Works in macOS 10.13. Jan 28, 2018 at 15:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .