Home > Jamf Pro > One Admin to Rule Them All

One Admin to Rule Them All

During JNUC 2022 the GOATs, Mark Buffington and Sean Rabbitt, presented “One Account to P0wn Them All: How to Move Away from a Shared Admin Account”. One of the workflows that they presented was to utilize the local admin account that is created during a PreStage enrollment as a local admin account for times when you need an admin account. You know, times like when you need to install software on a machine, or do some other admin task but don’t have a user account that is admin. There’s a better way to handle this with Jamf Connect and just in time provisioning of an admin account, but this workflow is for those that maybe are not using Jamf Connect, yet.

The workflow they outlined is to create the PreStage account and the Management Account that is used for User Initiated Enrollment (UIE) with the same password. Then using policies in Jamf Pro, after the Bootstrap Token has been escrowed to Jamf Pro, you can randomize this account password. By randomizing the password you prevent the same password from being on all of your devices. Then when you need to use that account for admin duties, you can use a Jamf Pro policy to change the password to a known password, do the needful, and then re-randomize the password. So how do we turn this into a workflow that is real world?

Note: This workflow is for devices that are enrolled via Automated Device Enrollment only. Can this workflow be adapted for UIE enrolled devices? Probably, but it would require the creation of our admin account along with the escrow of the Bootstrap token. If both of those can be accomodated, then it is possible this workflow could be adapted.

Scenario

We’re going to build out a Self Service method for our field techs and help desk agents to be able change the password for our hidden management/admin account to a known password (something we perhaps store in a password vault and rotate regularly). We’ll also create a script and LaunchDaemon that will run 30 minutes after the password is changed to reset it back to a randomized one. We will also create a Self Service method for them to reset the password back to a randomized one.

Setup

Following along with Mr. Buffington, and using the screenshot from his GitHub for the presentation, the first thing we need to do is create an Extension Attribute that will capture whether the Bootstrap Token has been escrowed to Jamf Pro or not. We need to insure the token is escrowed before we randomize the password, otherwise we could end up with the first SecureToken user being the admin with a randomized password, and that’s not a good idea. In a normal deployment, the Bootstrap token is created and escrowed when the first user signs into the computer interactively (via the login window or via SSH). 

Extension Attribute

The code for the Extension Attribute is the following:

#!/bin/bash

tokenStatus=$(profiles status -type bootstraptoken | awk '{ print $7 }' | sed 1d)
if [ $tokenStatus == "NO" ]
then
	echo "<result>Not Escrowed</result>"
elif [ $tokenStatus == "YES" ]
then
	echo "<result>Escrowed</result>"
else
	echo "<result>Unknown</result>"
fi

Smart Group

Now that we have an EA, let’s create a Smart Group to capture the devices that have escrowed their Bootstrap token. It’s pretty simple, we’re just going to look for “Escrowed” as the results of our EA.

Scripts

Ok, we’re gonna need a couple of policies and a couple of scripts. Let’s start with the scripts first.

The first script we are going to create will be utilized by the policy to set the password to a static, known value. The script will create a script on the target computer, along with a LaunchDaemon that will run the script we create after a 30 minute period. The script we create on the computer will simply trigger a policy to re-randomize the admin account password. This will make more sense when we see the script.

#!/bin/bash

#########################################################################################
#
# Copyright (c) 2022, JAMF Software, LLC.  All rights reserved.
#
# THE SOFTWARE IS PROVIDED "AS-IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
# JAMF SOFTWARE, LLC OR ANY OF ITS AFFILIATES BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT, OR OTHERWISE, ARISING FROM, 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN
# THE SOFTWARE, INCLUDING BUT NOT LIMITED TO DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# CONSEQUENTIAL OR PUNITIVE DAMAGES AND OTHER DAMAGES SUCH AS LOSS OF USE, PROFITS,
# SAVINGS, TIME OR DATA, BUSINESS INTERRUPTION, OR PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES.
#
#########################################################################################
#
#
# You will want to update the script path and script name to be what you would like it to be.
#
# Update these variables: script_path and script_name
# 
# You will want to update the name of the LaunchDaemon, along with the contents of the daemon
# to match the script path and name that you set.
# Update this variable: launchDaemon
#
#
#########################################################################################
## VARIABLES

script_path="/private/var/acme/scripts/"
script_name="changemgmtpass.sh"
script="$script_path$script_name"

launchDaemon="/Library/LaunchDaemons/com.acme.changeMgmtPass.plist"

#########################################################################################

# create the script on the local machine
# check for our scripts folder first
if [[  ! -d "$script_path" ]]
then
	/bin/mkdir -p "$script_path"
fi

tee "$script" << EOF
#!/bin/bash

# run randomize policy
/usr/local/jamf/bin/jamf policy -event changeMgmtPassword

# bootout launchd
/bin/launchctl bootout system "$launchDaemon" 2> /dev/null

# remove launchdaemon
rm -f "$launchDaemon"

rm -f "$script"

exit 0
EOF

# fix ownership
/usr/sbin/chown root:wheel "$script"

# Set Permissions
/bin/chmod +x "$script"

# now create LaunchDaemon
# Check to see if the file exists
if [[ -f "$launchDaemon" ]]
then
	# Unload the Launch Daemon and surpress the error
	/bin/launchctl bootout system "$launchDaemon" 2> /dev/null
	rm "$launchDaemon"
fi

tee "$launchDaemon" << 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>
	<key>Label</key>
	<string>$(basename "$launchDaemon" | sed -e 's/.plist//')</string>
	<key>ProgramArguments</key>
	<array>
		<string>/bin/bash</string>
		<string>/private/var/acme/scripts/changemgmtpass.sh</string>
	</array>
	<key>StartInterval</key>
	<integer>120</integer>
</dict>
</plist>
EOF

# Set Ownership
/usr/sbin/chown root:wheel "$launchDaemon"

# Set Permissions
/bin/chmod 644 "$launchDaemon"

# Load the Launch Daemon
/bin/launchctl bootstrap system "$launchDaemon"

exit 0

Now that we have that script in place, we will create a second script that can be run from a Self Service policy to run the policy to re-randomize the password. This policy can be run prior to the LaunchDaemon running and it will unload the LaunchDaemon and the LaunchDaemon and the script we stored on the system.

#!/bin/bash
#########################################################################################
#
# Copyright (c) 2022, JAMF Software, LLC.  All rights reserved.
#
# THE SOFTWARE IS PROVIDED "AS-IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
# JAMF SOFTWARE, LLC OR ANY OF ITS AFFILIATES BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT, OR OTHERWISE, ARISING FROM, 
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN
# THE SOFTWARE, INCLUDING BUT NOT LIMITED TO DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# CONSEQUENTIAL OR PUNITIVE DAMAGES AND OTHER DAMAGES SUCH AS LOSS OF USE, PROFITS,
# SAVINGS, TIME OR DATA, BUSINESS INTERRUPTION, OR PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES.
#
#########################################################################################
#
#
# You will want to update the script path and script name to be what you would like it to be.
#
# Update these variables: script_path and script_name
# 
# You will want to update the name of the LaunchDaemon, along with the contents of the daemon
# to match the script path and name that you set.
# Update this variable: launchDaemonPath
#
#
#########################################################################################
### Variables
script_path="/private/var/acme/scripts/"
script_name="changemgmtpass.sh"
script="$script_path$script_name"

launchDaemon="/Library/LaunchDaemons/com.acme.changeMgmtPass.plist"

# Run the management randomization policy
/usr/local/jamf/bin/jamf policy -event changeMgmtPassword

# now bootout the launch daemon we loadead and delete
# bootout launchd
/bin/launchctl bootout system "$launchDaemon" 2> /dev/null

# remove launchdaemon
rm -f "$launchDaemon"

# remove the script
rm "$script"

exit 0

Policies

Now that our scripts are in place we can create our policies. We are going to create four (4) policies:

  1. A policy to randomize the management account password on recurring check-in, once.
  2. A policy to randomize the management account password with a custom trigger and set to ongoing.
  3. A policy to change the management account password to a known static value, set to ongoing, and available in Self Service
  4. A policy to randomize the management account password via Self Service, set to ongoing.
Policy 1 – Randomize on check-in

The first policy will simply use the “Management Actions” policy payload set to “Change Account Password” and “Randomly generate new password”.

This policy will be scoped to our “Bootstrap Token Escrowed” Smart Group that we created at the begining. Set this policy to trigger on “Recurring Check-In” and set it to an “Execution Frequency” of “Once Per Computer”. The policy will trigger after the first user has signed into the computer for the first time.

Policy 2 – Randomize on custom event

The second policy can be created by cloning the first policy we created and changing the trigger and the frequency. Uncheck the “Recurring Check-in” trigger and instead check “Custom” and enter a value in the text box. For my policy I set this to “changeMgmtPassword”, but it can be whatever you want. Change the “Execution Frequency” to “Ongoing” and save the policy.

Why did we make those changes to the second policy? Well, we want this policy to be availble to our scripts, so we’re using the custom event, and we want it to run anytime we need it so we set the frequency to Ongoing. Since we will only call this policy via that custom event, we can be fairly certain knowing this policy will only run when we want it to.

Policy 3 – Change to static password via Self Service

We’re on to the third policy. This is the first of our Self Service policies. This policy will have no triggers since it is a Self Service policy, and we want the “Execution Frequency” to be set to “Ongoing”. We will add the first script we created to this policy (it doesn’t matter if it is set to Before or After). Head over to the “Management Actions” portion of the policy and in here you will set the known static password you want this account to use. 

Notice the warning we have above our password box. Best practice is for us to randomize the Management Account password, so that is why we’re letting you know this is a bad idea. But we’ll ignore it for now.

Head over to the Scope tab and we’ll set this one to our “Bootstrap Token Escrowed” Smart Group. While you’re here, we’re going to use a trick to hide this policy from most users. Click on the “Limitations” tab and then the Add button. Click on “LDAP User Groups” and add the group you have all of your techs in (you do have an LDAP group for all of your techs, right?). For me that group is named “Jamf Admins” but it can be whatever you want.

Why did we do that? Well, by adding that group as a limitation, a tech will need to login to Self Service so that the policy will be visible. This will prevent normal users from seeing that policy and running it. If you do not have login enabled for Self Service, you can read about it here. Also, you can set it so that users do not have to login to get into Self Service, just that a login button is available. You can also use the login method for scoping policies to users.

After the Scope is done, you can head over to the Self Service tab and setup the way the item will appear in Self Service. In the “Description” field you may want to put info about where the SuP3r SekReT password is stored. Maybe put in the fact that the password will re-randomize after 30 minutes (or whatever timeframe you want) and a reminder to run the Self Service policy to re-randomize.

Once you’re done there, go ahead and save this policy.

Policy 4 – Randomize password via Self Service

Our last policy to create, this policy will randomize the password via Self Service so that a tech can make sure when they are done the password is changed back. For this policy we will have no triggers, since it is Self Service, and the “Execution Frequency” will be set to “Ongoing”. We’ll be doing our work via the second script we created, so go ahead and attach that second script to this policy. Again, it doesn’t matter if it is set to “Before” or “After”.

On the Scope tab you have the choice of making so everyone sees it, or using our “Limitations” trick from Policy 3 to make it visible only to our techs. Scope to our “Bootstrap Token Escrowed” Smart Group and make your decision on the visibility.

Once you’ve done that, head over to the Self Service tab and setup the look of the policy in Self Service. Once you’re done, go ahead and save our policy.

What’s Next?

Now that we have all of the parts and pieces in place, how do we take advantage of this? Well, any computer that gets enrolled will have the Management Account created, and once the Bootstrap token gets escrowed that computer can take advantage of this workflow. A tech will be able to walk up to the computer, open Self Service, login to Self Service, and then utilize our static password policy to use that Management Account to do the needful.

If you wanted to store what type of password (random or static) was in use, you could use a “reverse Extension Attribute” to do that. Basically, store a value in a plist on the computer indicating if the password is “S”tatic or “R”andom. Then use an Extension Attribute to grab that value. You could put this in the scripts that we created above (make sure to include a recon so the value gets into Jamf Pro).

You can find the screenshots, scripts, and the XML of the Extension Attribute in my GitHub repository here.

Categories: Jamf Pro Tags: , , ,
  1. Keith Myers
    November 29, 2022 at 7:25 pm

    Thank you for this! I was in that session and thought it would be really cool to see how it would work.

    The Jamf documentation explicitly says not to use the management account to log on locally. Is that written to deter people from changing it to a known password from a random password, or is there another reason it shouldn’t be used that way?

    • April 24, 2023 at 3:14 pm

      Primarily to deter from using a known password that could be hacked. Think about it, this account is on every device enrolled in Jamf Pro. So one account across all of your devices with a known password is a big security risk.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: