A collection of racing stripes for BeyondTrust Endpoint Privilege Management on macOS
Vendor Overview
BeyondTrust Endpoint Privilege Management allows organizations to:
Enforce least privilege dynamically to prevent malware, ransomware, and identity-based attacks, achieve compliance across Windows, macOS, and Linux endpoints, and enable your zero trust strategy — without compromising on productivity.
Racing Stripes
The following racing stripes proved helpful in our initial deployment and ongoing support of BeyondTrust Endpoint Privilege Management for macOS.
1. Local Administrative Rights Remove.bash
For better or for worse, BeyondTrust Endpoint Privilege Management does not (currently) manage the task of changing your users from local Administrators to Standard users and the Local Administrative Rights Remove.bash
script may prove helpful for this task.
After modifying approvedAdmins
— the space-delimited list of approved administrators — add the script to your Jamf Pro server.
#################################################################################################### # # Global Variables # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # scriptVersion="2.0.4" export PATH=/usr/bin:/bin:/usr/sbin:/sbin scriptLog="${4:-"/var/log/org.churchofjesuschrist.log"}" # Parameter 4: Script Log Location approvedAdmins=(root _avectodaemon yourLocalAdmin) # Space-delimited list of approved local admins currentAdminUsers=$( /usr/bin/dscl . -read Groups/admin GroupMembership | /usr/bin/cut -c 18- ) currentAdminArray=("$currentAdminUsers") adminRemovalFailures=""
Critical BeyondTrust-related processes are confirmed running before actually removing local administrative rights.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for BeyondTrust Privilege Access Management for macOS services # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # currentLoggedInUser # Validate various BT PMfM Processes procesStatus "defendpointd" procesStatus "Custodian" # procesStatus "PMCAdapter" procesStatus "PMCPackageManager" procesStatus "PrivilegeManagement" procesStatus "NewPrivilegeManagement" updateScriptLog "Status: ${RESULT}" case ${RESULT} in *"NOT"* ) updateScriptLog "At least one service isn't running; exiting …" exit 1 ;; * ) updateScriptLog "All services running; proceeding …" ;; esac
Local Administrative Rights Remove.bash
Latest version available on GitHub.
#!/bin/bash #################################################################################################### # # ABOUT # # Removes admin rights from adminstrators NOT specificially listed in `approvedAdmins` # #################################################################################################### # # HISTORY # # Version 1.0.0, 08-May-2023, Dan K. Snelson (@dan-snelson) # Original version # # Version 2.0.0, 20-May-2023, Dan K. Snelson (@dan-snelson) # Confirm BeyondTrust Privilege Access Management for macOS services are running before # removing local admin rights # # Version 2.0.1, 10-Nov-2023, Dan K. Snelson (@dan-snelson) # - When no users are logged in, only check for two (2) BT PMfM services # - Check that `dseditgroup` succeeded # # Version 2.0.2, 25-Nov-2023, Dan K. Snelson (@dan-snelson) # - Added check for PMCAdapter # # Version 2.0.3, 20-May-2024, Dan K. Snelson (@dan-snelson) # - Changed $adminMemberCheck to leverage `dseditgroup checkmember` # # Version 2.0.4, 07-Jun-2024, Dan K. Snelson (@dan-snelson) # - Commented-out check for "PMCAdapter" in favor of check for "PMCPackageManager" # #################################################################################################### #################################################################################################### # # Global Variables # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # scriptVersion="2.0.4" export PATH=/usr/bin:/bin:/usr/sbin:/sbin scriptLog="${4:-"/var/log/org.churchofjesuschrist.log"}" # Parameter 4: Script Log Location approvedAdmins=(root _avectodaemon yourLocalAdmin) # Space-delimited list of approved local admins currentAdminUsers=$( /usr/bin/dscl . -read Groups/admin GroupMembership | /usr/bin/cut -c 18- ) currentAdminArray=("$currentAdminUsers") adminRemovalFailures="" #################################################################################################### # # Pre-flight Checks # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ ! -f "${scriptLog}" ]]; then touch "${scriptLog}" fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Script Logging Function # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function updateScriptLog() { echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Logging Preamble # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "\n\n###\n# Local Administrative Rights Remove (${scriptVersion})\n###\n" updateScriptLog "PRE-FLIGHT CHECK: Initiating …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Confirm script is running as root # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ $(id -u) -ne 0 ]]; then updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting." exit 1 else updateScriptLog "PRE-FLIGHT CHECK: Complete" fi #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Current Logged-in User Function # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function currentLoggedInUser() { loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' ) updateScriptLog "Current Logged-in User: ${loggedInUser}" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for running processes (supplied as Parameter 1) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function procesStatus() { processToCheck="${1}" status=$( /usr/bin/pgrep -x "${processToCheck}" ) if [[ -n ${status} ]]; then RESULT+="'${processToCheck}' running; " else RESULT+="'${processToCheck}' NOT running; " fi } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Remove unapproved admins # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function adminRemove() { adminAccountToCheck="${1}" if [[ " ${approvedAdmins[*]} " =~ " ${adminAccountToCheck} " ]]; then updateScriptLog "Not changing approved admin '${adminAccountToCheck}'." else dseditgroup -o edit -d "${adminAccountToCheck}" -t user admin if [[ $? = 0 ]]; then updateScriptLog "Attempted to remove user '${adminAccountToCheck}' from admin group."; fi fi } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for approved admins only # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function adminCheck() { standardAccountToCheck="${1}" if [[ " ${approvedAdmins[*]} " =~ " ${standardAccountToCheck} " ]]; then updateScriptLog "Approved admin '${standardAccountToCheck}' unchanged." else # adminMemberCheck=$( dsmemberutil checkmembership -U "${standardAccountToCheck}" -G admin ) adminMemberCheck=$( dseditgroup -o checkmember -m "${standardAccountToCheck}" admin ) updateScriptLog "Unapproved admin '${standardAccountToCheck}' unchanged." updateScriptLog "'${standardAccountToCheck}' ${adminMemberCheck} 'admin'." adminRemovalFailures+="${standardAccountToCheck} " fi } #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for BeyondTrust Privilege Access Management for macOS services # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # currentLoggedInUser # Validate various BT PMfM Processes procesStatus "defendpointd" procesStatus "Custodian" # procesStatus "PMCAdapter" procesStatus "PMCPackageManager" procesStatus "PrivilegeManagement" procesStatus "NewPrivilegeManagement" updateScriptLog "Status: ${RESULT}" case ${RESULT} in *"NOT"* ) updateScriptLog "At least one service isn't running; exiting …" exit 1 ;; * ) updateScriptLog "All services running; proceeding …" ;; esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Remove Local Administrative Rights # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "\n\nAttempting to remove unapproved admins …" for currentAdminAccount in ${currentAdminArray[@]}; do adminRemove "${currentAdminAccount}" done # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check Local Administrative Rights # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "\n\nChecking for unapproved admins …" updatedAdminUsers=$( /usr/bin/dscl . -read Groups/admin GroupMembership | /usr/bin/cut -c 18- ) updatedAdminArray=("$updatedAdminUsers") for updatedAdminAccount in ${updatedAdminArray[@]}; do adminCheck "${updatedAdminAccount}" done # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Exit # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ -n "${adminRemovalFailures}" ]]; then updateScriptLog "\n\nERROR: Unapproved admin: ${adminRemovalFailures}" exit 1 else updateScriptLog "\n\nSuccess! No unapproved admins." exit 0 fi
2. Message: Body Options
Without question, modifying the Message: Body Options to use many of the built-in BeyondTrust EPM variables proved to be the one best change we made from the QuickStart for macOS policy.
Use of this [PG_PROG_TYPE] is audited. Are you sure you wish to proceed? (See KB8675309.) Computer: [PG_COMPUTER_NAME] Workstyle: [PG_POLICY_NAME] Application Group: [PG_APP_GROUP] Application Rule: [PG_APP_DEF] Application: • Name: [PG_PROG_NAME] • Version: [PG_PROG_PROD_VERSION]
Also, the addition of enabling Show Reference Hyperlink continues to reduce untold numbers of support requests.
4. Jamf Pro Extension Attributes
We’re currently leveraging three Jamf Pro Extension Attributes:
- Adapter Version
- Assigned Flexibility
- Health Check
BeyondTrust PMfM Adapter Version
#!/usr/bin/env bash ########################################################################## # A script to collect the version of PMCAdapter.app currently installed. # # If PMCAdapter.app is not installed, "Not Installed" will returned. # ########################################################################## RESULT="Not Installed" testFile="/usr/local/libexec/Avecto/iC3Adapter/1.0/PMCAdapter.app/Contents/Info.plist" if [ -f "${testFile}" ] ; then RESULT=$( /usr/bin/defaults read "${testFile}" CFBundleVersion ) fi /bin/echo "<result>$RESULT</result>"
BeyondTrust PMfM Assigned Flexibility
Latest version available on GitHub.
#!/bin/zsh --no-rcs # shellcheck shell=bash #################################################################################################### # # ABOUT # # BeyondTrust PMfM Workstyle # # Determines the last logged-in user's assigned BeyondTrust Workstyle. # See: [BeyondTrust EPM: Flexibilities](https://snelson.us/2024/08/beyondtrust-epm-flexibilities/) # #################################################################################################### # # HISTORY # # Version 0.0.1, 17-Aug-2024, Dan K. Snelson (@dan-snelson) # - Original Version (inspired by @Mike Wolf) # # Version 0.0.2, 19-Aug-2024, Dan K. Snelson (@dan-snelson) # - Parse audit.log for more recently reported Workstyle (inspired by @tziegmann) # #################################################################################################### #################################################################################################### # # Variables # #################################################################################################### scriptVersion="0.0.2" export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ # Last Logged-in User lastUser=$( defaults read /Library/Preferences/com.apple.loginwindow.plist lastUserName ) # Last Logged-in User's Group Membership lastUserGroupMembership=$( id -Gn "${lastUser}" ) #################################################################################################### # # Program # #################################################################################################### case "${lastUserGroupMembership}" in *"high"* ) echo "<result>High</result>" ;; *"medium"* ) echo "<result>Medium</result>" ;; *"low"* ) echo "<result>Low</result>" ;; * ) workstyle=$( grep '"Workstyle":' /var/log/defendpoint/audit.log | tail -n 1 | awk -F'": "' '{print $3,$NF}' | sed 's/",//g; s/^ *//g' ) echo "<result>${workstyle}</result>" ;; esac exit 0
BeyondTrust PMfM Health Check
Latest version available on GitHub.
#!/bin/bash #################################################################################################### # # ABOUT # # A Jamf Pro Extension Attribute which determines the health of BeyondTrust Privilege Management for Mac # # If the `targetPolicy` is not found, "Not Installed" will be returned. # # If the `targetPolicy` is found, the following is returned: # # - Policy # - Name # - Revision Number # - System Extension # - Process Status # - defendpointd # - Custodian # - PMCAdapter # #################################################################################################### # # HISTORY # # Version 0.0.1, 22-Nov-2023, Dan K. Snelson (@dan-snelson) # Original version # # Version 0.0.2, 25-Nov-2023, Dan K. Snelson (@dan-snelson) # Added Tom Ziegmann-inspired racing-stripes # # Version 0.0.3, 27-Nov-2023, Dan K. Snelson (@dan-snelson) # Added Andrew Spokes-inspired racing-stripe # Added System Extension check # # Version 0.0.4, 28-Nov-2023, Dan K. Snelson (@dan-snelson) # Added Eric Hemmeter-inspired racing-stripe # Output edits (i.e., removed "Policy:") # # Version 0.0.5, 22-Aug-2024, Dan K. Snelson (@dan-snelson) # Added checksum of "${targetPolicy}" # #################################################################################################### #################################################################################################### # # Variables # #################################################################################################### scriptVersion="0.0.5" export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ targetPolicy="/etc/defendpoint/ic3.xml" #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for running processes (supplied as Parameter 1) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function procesStatus() { processToCheck="${1}" status=$( /usr/bin/pgrep -x "${processToCheck}" ) if [[ -n ${status} ]]; then processCheckResult+="'${processToCheck}' running; " else processCheckResult+="'${processToCheck}' NOT running; " fi } #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Validate Presence of Target Policy # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ -f "${targetPolicy}" ]] ; then # Validate BT PMfM System Extension systemExtensionTest=$( systemextensionsctl list | awk -F"[][]" '/com.beyondtrust.endpointsecurity/ {print $2}' ) # Capture BT PMfM Policy Name and Revision policyName=$( xmllint --xpath "string(//@PolicyName)" "${targetPolicy}" ) policyRevision=$( xmllint --xpath "string(//@RevisionNumber)" "${targetPolicy}" ) # Capture BT PMfM Policy Checksum policyChecksum=$( openssl dgst -sha256 "${targetPolicy}" | awk -F'= ' '{print $2}' ) # Validate various BT PMfM Processes procesStatus "defendpointd" procesStatus "Custodian" procesStatus "PMCAdapter" # procesStatus "PrivilegeManagement" # Appears to only run if a user is logged-in # Remove trailing "; " processCheckResult=${processCheckResult/%; } RESULT="${policyName} (r${policyRevision}); System Extension: ${systemExtensionTest}; ${processCheckResult}; Policy Checksum: ${policyChecksum}" else RESULT="Not Installed" fi echo "<result>${RESULT}</result>" exit 0
6. SYM Validation: BeyondTrust Privileged Access Management.bash
A Setup Your Mac Remote Validation which determines the health of BeyondTrust Privilege Management for Mac during initial installation.
Latest version available on GitHub.
#!/bin/bash #################################################################################################### # # ABOUT # # A script which determines the health of BeyondTrust Privilege Management for Mac # #################################################################################################### # # HISTORY # # Version 0.0.1, 22-Dec-2023, Dan K. Snelson (@dan-snelson) # Original version # # Version 0.0.2, 07-Jun-2024, Dan K. Snelson (@dan-snelson) # Updates for BT PMfM 24.x # #################################################################################################### #################################################################################################### # # Variables # #################################################################################################### scriptVersion="0.0.2" export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Check for running processes (supplied as Parameter 1) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function procesStatus() { processToCheck="${1}" status=$( /usr/bin/pgrep -x "${processToCheck}" ) if [[ -n ${status} ]]; then processCheckResult+="'${processToCheck}' Running; " else processCheckResult+="'${processToCheck}' Failed; " fi } #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Validate BeyondTrust Privilege Management for Mac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Validate BT PMfM System Extension systemExtensionTest=$( systemextensionsctl list | awk -F"[][]" '/com.beyondtrust.endpointsecurity/ {print $2}' ) case "${systemExtensionTest}" in "activated enabled" ) processCheckResult="'System Extension' Running; " ;; * ) processCheckResult="'System Extension' Failed; " ;; esac # Validate various BT PMfM Processes procesStatus "defendpointd" procesStatus "Custodian" # procesStatus "PMCAdapter" procesStatus "PMCPackageManager" procesStatus "PrivilegeManagement" procesStatus "NewPrivilegeManagement" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Output Results # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Remove trailing "; " processCheckResult=${processCheckResult/%; } case "${processCheckResult}" in *"Failed"* ) RESULT="At least one service failed: ${processCheckResult}" ;; * ) RESULT="All Services Running" ;; esac /bin/echo "<result>${RESULT}</result>"
7. Privilege Management Configuration Profile for macOS (2.1.2)
Thankfully, BeyondTrust distributes a nearly complete Configuration Profile which should — in my not so humble opinion — include the Notifications payload (and then also be vendor-signed to significantly reduce the possibility of Jamf Pro admins improperly configuring the entire EPM solution).
- Notifications
- BeyondTrust Privilege Management:
com.avecto.defendpoint
- NewPrivilegeManagement.app:
com.beyondtrust.epm.gui
- BeyondTrust Privilege Management:
8. QuickStart for macOS+
One of the selling points for BeyondTrust Endpoint Privilege Management is the robust macOS QuickStart template which makes initial deployment an absolute dream.
The QuickStart for macOS policy contains Workstyles, Application Groups, and Messages configured with Endpoint Privilege Management for Mac and Application Control.
The QuickStart policy has been designed from BeyondTrust’s experiences of implementing the solution across thousands of customers, and is intended to balance security with user freedom.
As every environment is different, we recommend you thoroughly test this configuration to ensure it complies with the requirements of your organization.
After you’ve gained access to BeyondTrust Customer Portal, I recommend you validate your QuickStart template includes recent changes from the following vendor-authored articles:
KB0017244
How to allow printing in Privilege Management for MacKB0018815
Best practices when using the QuickStart for Mac policy templateKB0018817
Endpoint Privilege Management for Mac QuickStart policy changes for System SettingsKB0019301
Installing macOS updates on Apple silicon hardwareKB0020070
How to allow devices to unmount (eject) using Endpoint Privilege Management for Mac (EPM-M)KB0021267
How to allow modification of Keychain Access with EPM-M
9. macOS Sequoia 15 and “missing” EPM accounts
Overview
When upgrading from macOS Sonoma 14.6.1 to macOS Sequoia 15.0 (24A5331b) Beta 8, two critical EPM-related accounts are removed during the OS upgrade process and can be re-added by re-deploying the BeyondTrust EPM client:
_avectodaemon
_defendpoint
Post-upgrade to macOS Sequoia 15
> sw_vers ; echo "BT PMfM Accounts: $( dscl . list /Users | grep -E "(_avectodaemon|_defendpoint)" )" ProductName: macOS ProductVersion: 15.0 BuildVersion: 24A5331b BT PMfM Accounts:
Re-deploy BeyondTrust EPM Client
sw_vers ; echo "BT PMfM Accounts: $( dscl . list /Users | grep -E "(_avectodaemon|_defendpoint)" )" ProductName: macOS ProductVersion: 15.0 BuildVersion: 24A5331b BT PMfM Accounts: _avectodaemon _defendpoint
Support
While unofficial, best-effort support is available on the Mac Admins Slack (free, registration required) #beyondtrust-priv-man Channel, it’s probably best to direct-message me on Slack if your mileage varies too greatly.