Posted on 01-10-2024 05:40 PM
I have a Smart Group monitoring computers where the bootstrap token isn't escrowed. In the past week, I noticed a couple freshly wiped + re-enrolled computers (all on Sonoma 14.2 and 14.2.1), where the first user logging into the computer isn't getting the Secure Token or Volume Ownership. Also, the bootstrap token doesn't get escrowed.
Background: in our 1:1 deployments, we have a PreStage-created, hidden local admin account. In this 1:1 PreStage, we have enrollment customization turned on, pointing to our SSO IdP. User gets a device, logs in through SSO, which populates their info into the local user creation screen. They enter their password again, and get logged into the device.
Enrollment finishes, and for some reason Jamf is showing the PreStage admin account as the only Secure Token holder and Volume Owner. And as mentioned, bootstrap Token doesn't get escrowed. I can fix manually using sudo profiles install -type bootstraptoken, but trying to figure out why this is happening? Administrator has never been signed into. And the account that is the first one logging in, isn't getting secure token / volume ownership.
Strangely, it's not quite 100% of the time. I wiped a 14.2.1 tester M1 and re-enrolled, it went as expected. I got volume ownership, secure token, and bootstrap was escrowed. Same exact PreStage, same policies, profiles, etc.
PreStage hasn't been touched lately. Yet this problem seems to have just started happening in the last week or so I believe. I put a ticket in and posted about this on the MacAdmins slack, just figured I'd post here as well.
Solved! Go to Solution.
Posted on 09-04-2024 01:28 PM
So this ended up probably being something on our end. We were getting away from having Jamf create a Local Admin account during PreStage and making one with a script + policy instead. Due to the whole impending forced LAPS changes and the fact that we were already using macOSLAPS, etc.
There was an unforeseen race condition where it was possible for our Admin account to get created before the 1:1 user's (who the computer is getting issued to) account got created. So our script/policy-created Admin account would get UID 501 instead of the 1:1 user.
The natural process of the bootstrap token getting escrowed and stuff is expecting the 1:1 user to have a UID of 501, not our admin account. So the bootstrap token wouldn't get escrowed, and our admin account could 'steal' the securetoken and be the sole holder.
I ended up implementing a check in the script that creates our admin account + installs macOSLAPS that ensures the 1:1 users account already exists with a UID of 501 and is a securetoken holder. It will wait and retry if the 1:1 users account hasn't been created yet when it runs. I also made it so that script/policy runs last during the enrollment process.
I've had essentially zero issues with the bootstrap token getting escrowed and weird secure token behavior since then.
Posted on 01-11-2024 04:49 AM
Let us know what you find. Saw some strangeness with M3 Mac on 14.2.1 and 14.2. Couldn't downgrade, so couldn't test different OS.
Had trouble with SecureToken, even with Bootstrap Token escrowed. Could be something I'm missing, but it all worked prior to 14.2+.
Posted on 09-04-2024 10:45 AM
Any updates on this? having similar issues.
Posted on 09-04-2024 01:28 PM
So this ended up probably being something on our end. We were getting away from having Jamf create a Local Admin account during PreStage and making one with a script + policy instead. Due to the whole impending forced LAPS changes and the fact that we were already using macOSLAPS, etc.
There was an unforeseen race condition where it was possible for our Admin account to get created before the 1:1 user's (who the computer is getting issued to) account got created. So our script/policy-created Admin account would get UID 501 instead of the 1:1 user.
The natural process of the bootstrap token getting escrowed and stuff is expecting the 1:1 user to have a UID of 501, not our admin account. So the bootstrap token wouldn't get escrowed, and our admin account could 'steal' the securetoken and be the sole holder.
I ended up implementing a check in the script that creates our admin account + installs macOSLAPS that ensures the 1:1 users account already exists with a UID of 501 and is a securetoken holder. It will wait and retry if the 1:1 users account hasn't been created yet when it runs. I also made it so that script/policy runs last during the enrollment process.
I've had essentially zero issues with the bootstrap token getting escrowed and weird secure token behavior since then.
Posted on 09-17-2024 07:53 AM
Can you share this script, we have something similar happening
09-17-2024 12:44 PM - edited 09-17-2024 12:45 PM
Sure. The initial password for our LAPS account is fed through a policy parameter and is encoded.
#!/bin/bash
# Ingest old Administrator password (encoded) so that if something goes wrong we have a fallback
ENCODED_PASSWORD="$4"
DECODED_PASSWORD=$(echo "${ENCODED_PASSWORD}" | base64 -D)
# First check to ensure the 1:1 user has been created *and* has SecureToken issued before creating Local Admin account
# Maximum number of retries
MAX_RETRIES=20
RETRY_COUNT=0
while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do
# Check for the existence of the primary user with UID 501
PRIMARY_USER=$(dscl . -list /Users UniqueID | awk '$2 == 501 {print $1}')
if [[ -n "$PRIMARY_USER" ]]; then
# Check if the primary user has a SecureToken
PRIMARY_USER_TOKEN_STATUS=$(sysadminctl -secureTokenStatus $PRIMARY_USER 2>&1)
if [[ $PRIMARY_USER_TOKEN_STATUS =~ "ENABLED" ]]; then
echo "Primary user with UID 501 has SecureToken, proceeding with admin account creation."
# Create the Administrator user using jamf binary
sudo jamf createAccount -username Administrator -realname "Administrator" -password "${DECODED_PASSWORD}" -home /Users/Administrator -admin
# Manually hide the Administrator user account
sudo dscl . create /Users/Administrator IsHidden 1
# Trigger the macOSLAPS policy
sudo jamf policy -event macoslaps
exit 0
else
echo "Primary user with UID 501 does not have SecureToken. Retrying in 30 seconds..."
fi
else
echo "Primary user with UID 501 not found. Retrying in 30 seconds..."
fi
# Wait for 30 seconds before retrying
sleep 30
let RETRY_COUNT=RETRY_COUNT+1
done
echo "Failed to verify SecureToken for UID 501 after $MAX_RETRIES attempts. Exiting script."
exit 1
Admin / LAPS account only gets created after the 1:1 users account has already been created, and they're confirmed to have a SecureToken. Once the Admin account has been created it triggers our separate policy that installs macOSLAPS and rolls the password.
We use this same script for shared Jamf Connect devices as well. Only difference there is I have a script that escrows the Bootstrap Token prior to this script running, so that our technicians don't have to manually login to each shared device after enrolling them.
09-18-2024 07:39 AM - edited 09-18-2024 07:40 AM
FYI I ended up making a script to manually set a password for our laps accounts on apple silicone to fix some issues with volume ownership based on a smart group.
#!/bin/bash
# API USER
user="YOUR_API_USERNAME_HERE"
# API PASSWORD
pass="YOUR_API_USER_PASSWORD_HERE"
# URL (https://yourjamfserver.jamfcloud.com)
jurl="https://YOUR_JAMF_URL_HERE"
# Smart group or static group ID to get computer IDs from
groupID="YOUR_SMARTGROUP_ID_HERE"
# Define the admin user name
adminname="YOUR_ADMIN_USERNAME_HERE"
# New LAPS password to set
newPassword="YOUR_PASSWORD_HERE"
# Get Bearer token for API calls
getBearerToken() {
response=$(curl -s -u "$user:$pass" "$jurl/api/v1/auth/token" -X POST)
token=$(echo "$response" | plutil -extract token raw -)
tokenExpiration=$(echo "$response" | plutil -extract expires raw - | awk -F . '{print $1}')
tokenExpirationEpoch=$(date -j -f "%Y-%m-%dT%T" "$tokenExpiration" +"%s")
echo "Token acquired."
}
# Invalidate the token once done
invalidateToken() {
curl -w "%{http_code}" -H "Authorization: Bearer $token" "$jurl/api/v1/auth/invalidate-token" -X POST -s -o /dev/null
echo "Token invalidated."
}
# Check token expiration and get a new one if necessary
checkTokenExpiration() {
nowEpochUTC=$(date -j -f "%Y-%m-%dT%T" "$(date -u +"%Y-%m-%dT%T")" +"%s")
if [[ tokenExpirationEpoch -gt nowEpochUTC ]]; then
echo "Token is valid."
else
echo "Token expired, fetching new token."
getBearerToken
fi
}
# Run the function to get the token
getBearerToken
# Grab all computer IDs from the smart group (ID 802)
echo "Fetching computer IDs from group ID: $groupID"
computerids=($(curl -s $jurl/JSSResource/computergroups/id/$groupID \
-H 'accept: application/xml' \
-H "Authorization: Bearer $token" | xmllint --xpath '/computer_group/computers/computer/id/text()' -))
echo "Found Computer IDs: ${computerids[@]}"
# Loop through all computer IDs to look up the managementId and reset the LAPS password
for id in "${computerids[@]}"; do
checkTokenExpiration
# Get computer details to retrieve the managementId
computerInfo=$(curl -s "$jurl/api/v1/computers-inventory/$id?section=GENERAL" \
-H 'accept: application/json' \
-H "Authorization: Bearer $token")
# Extract the managementId using jq
managementId=$(echo "$computerInfo" | jq -r '.general.managementId')
if [[ -z "$managementId" || "$managementId" == "null" ]]; then
echo "No management ID found for computer $id, skipping..."
continue
fi
echo "Found Management ID: $managementId for computer $id"
# Reset the LAPS password for the macadmin user using the correct API endpoint
curl -s -X PUT "$jurl/api/v2/local-admin-password/$managementId/set-password" \
-H "accept: application/json" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
-d "{\"lapsUserPasswordList\": [{\"username\": \"$adminname\", \"password\": \"$newPassword\"}]}" || echo "Failed to reset LAPS password for computer $id with Management ID: $managementId"
done
# Invalidate the token when done
invalidateToken
exit 0