File Vault

emptypony61
New Contributor III

Just wanted to walk through a scenario with file vault.

 

I have user A login with Jamf Connect to Mac and setup file vault. User A works for a few months then gets a better job and user B starts. User B logs in with Jamf Connect Creds. Does FileVault need to be reset for User B?

1 ACCEPTED SOLUTION

AJPinto
Honored Contributor II

Thankfully I work in finance so we don't deal with HIPAA lol, so I cannot say for sure. I'll explain the work flow we use. 

 

Our JAMF instance puts a local admin account on our Macs. I have the user name and password (salted) for that account which does have a FV token provided in the script for the "person" giving the filevault token. The script then tosses a popup to the user asking for their user name and password and puts it in variables to grant FileVault access. At no point do I nor anyone other than the user know their password, and at no point does the user know the password to the local account. Now if that is up to snuff for HIPAA unfortunately I am not sure. Someone with access to the script in JAMF could adjust the script to echo the user name and password to a log which could be a massive red flag, but security endpoint applications can monitor for that kind of nonsense. 

This is the script I tossed together if you wanted to look through it. The string decrypt part could be replaced with the local account password directly, or replaced with however you want to encrypt the password. Just adjust the variables as necessary. We bind to AD, all that can be removed since you use JAMF Connect so I'm assuming you dont AD bind.

 

 

#!bin/bash

######################
# Script Name: 
# Author: 
# Date: 10.1.2020
# Enhancements
######################

######################
# Exit Codes
# 0 - Sucess
# 1 - Failed: Admin account credentials are not correct
# 2 - Failed: Mac not domain bound, or otherwise cannot talk to the domain controller
# 3 - Failed: User account to be cached not found in Active Directory
# 4 - Failed: Mobile account failed to cache correctly
###################### 

echo "Begin script"

######################
# Gather and verify admin account
######################

#*------------------------ STRING DECRYPTION ------------------------*#

function DecryptString() {
    echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}"
}

Pass="encryption_nonsense"
Salt="encryption_nonsense"
DecryptString=$(DecryptString "$5" "$Salt" "$Pass") #Decrypt string is parameter 5 in the policy
        

adminUser="$4"
adminPass="$DecryptString"

osvers=$(sw_vers -productVersion | awk -F. '{print $2}')
check4AD=`/usr/bin/dscl localhost -list . | grep "Active Directory"`

## verify that adminuser and pass variables are both passed to the user
if [[ -z "$adminUser" ]] || [[ -z "$adminPass" ]] ; then
    dialog="either Admin User or Password is missing"
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 1
fi

## check the admin password
adminCheck=$(/usr/bin/dscl /Local/Default -authonly "$adminUser" "$adminPass")
if [[ -z "$adminCheck" ]] ; then
    echo "Admin password is verified"
else
    echo "Admin Password not working"
    exit 1
fi


######################
# Check for domain bind, fails out of not bound
######################

echo "Checking for Domain Bind"


if [ "${check4AD}" != "Active Directory" ]; then
    dialog="This machine is not bound to Active Directory.\nPlease bind to AD first. "
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 2
fi

######################
#Popups asking for user to ender userID and Password
######################

echo "Prompting for userToAdd credentials."

## Prompt for Username
userToAdd=$(/usr/bin/osascript<<END
tell application "System Events"
activate
set the answer to text returned of (display dialog "Enter the username to add to this computer" default answer "" buttons {"Continue"} default button 1)
end tell
END
)

## Prompt for Password
userPass=$(/usr/bin/osascript<<END
tell application "System Events"
activate
set the answer to text returned of (display dialog "Please enter the user's account Password:" default answer "" with hidden answer buttons {"Continue"} default button 1)
end tell
END
)

loopCount=0
while [ "$loopCount" -lt 3 ]; do
    # Refresh Directory Services
    if [[ ${osvers} -ge 7 ]]; then
        /usr/bin/killall opendirectoryd
    else
        /usr/bin/killall DirectoryService
    fi
    sleep 15

    ## try to auth the user in advance. this seems to increase the success of the ID command.
    /usr/bin/dscl /Search -authonly "$userToAdd" "$userPass"

    adCheck=`id $userToAdd`
    echo "AD Check is: $adCheck"
    if [[ -z "$adCheck" ]] ; then
        ((loopCount++))
    else
        echo "AD Check successful"
        break
    fi
done 

######################
# Fails script if user account is not found on domain controller
######################

if [[ -z "$adCheck" ]] ; then
    dialog="AD User Not found. Please contact whoever you want to put in this popup box at line 128"
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 3
fi


sleep 2

######################
# Create the new Mobile Account
######################

## hit AD create the user
/System/Library/CoreServices/ManagedClient.app/Contents/Resources/createmobileaccount -a "$adminUser" -U "$adminPass" -n "$userToAdd"

sleep 2

## check to see if the user account was added
userCheck=$(dscl . list /Users | grep "$userToAdd")

if [[ -z "$userCheck" ]] ; then
    dialog="AD User failed to add."
    echo "dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 4
fi

## this should query AD to cache the user including the password
dscacheutil -q user -a name "$userToAdd"

## this will auth the user to AD and should also cache their password locally
/usr/bin/dscl /Search -authonly "$userToAdd" "$userPass"

## this kills the menubar so that the fast user switching list refreshes
killall -KILL SystemUIServer

echo "Creating mobile account for $userToAdd."

######################
# Remove FV Access if existing
###################### 

sleep 5
sudo fdesetup remove -user $userToAdd

######################
# Provision Admin Access
###################### 

dscl . -append /groups/admin GroupMembership $userToAdd
echo "Provisioning admin access for $userToAdd."

######################
# Provision FileVault Access
###################### 

sleep 5
## Get the user to be added to FV
userName=$userToAdd

echo "Adding $userToAdd to FileVault 2 list."

## This "expect" block will populate answers for the fdesetup prompts that normally occur while hiding them from output
expect -c "
log_user 0
spawn fdesetup add -usertoadd $userName
expect \"Enter the primary user name:\"
send ${adminUser}\r
expect \"Enter the password for the user '$adminUser':\"
send ${adminPass}\r
expect \"Enter the password for the added user '$userName':\"
send ${userPass}\r
log_user 1
expect eof
"

echo "${userName} has been added to the FileVault 2 list."

######################
# Runs JAMF Recon to assign Mac to $userToAdd
###################### 

jamf recon -endUsername $userToAdd

######################
# Clean up and reboot
###################### 

sudo jamf recon

sleep 30

echo "triggering reboot policy, mac will reboot in 1 minute"

sudo jamf policy -event Force_Restart_Delay

echo "Script completed"

exit 0

 

 

 

View solution in original post

5 REPLIES 5

AJPinto
Honored Contributor II

In my opinion it is best practice to reinstall macOS between users. If you are "refreshing" (reinstalling macOS) between users it should be a new setup experience for each person down the line. User A leaves your company and should send the Mac back to your IT department, not directly to user B. Your IT department wipes the Mac and preps it for redeployment. When user B comes along they get a new install of MacOS with no remanence of user A's existence and go through the "new" device setup.

 

To hand devices around to my understanding you still need someone with a FileVault token to give someone new a FileVault token. Apple retired the ability to give FileVault tokens off a bootstrap token back with Mojave. This can be scripted but you must pass the password of a FileVault enabled user in the script. This would be the general work flow if you are not reinstalling macOS.

emptypony61
New Contributor III

Can you do pass a token and still meet HIPAA Compliance? 

AJPinto
Honored Contributor II

Thankfully I work in finance so we don't deal with HIPAA lol, so I cannot say for sure. I'll explain the work flow we use. 

 

Our JAMF instance puts a local admin account on our Macs. I have the user name and password (salted) for that account which does have a FV token provided in the script for the "person" giving the filevault token. The script then tosses a popup to the user asking for their user name and password and puts it in variables to grant FileVault access. At no point do I nor anyone other than the user know their password, and at no point does the user know the password to the local account. Now if that is up to snuff for HIPAA unfortunately I am not sure. Someone with access to the script in JAMF could adjust the script to echo the user name and password to a log which could be a massive red flag, but security endpoint applications can monitor for that kind of nonsense. 

This is the script I tossed together if you wanted to look through it. The string decrypt part could be replaced with the local account password directly, or replaced with however you want to encrypt the password. Just adjust the variables as necessary. We bind to AD, all that can be removed since you use JAMF Connect so I'm assuming you dont AD bind.

 

 

#!bin/bash

######################
# Script Name: 
# Author: 
# Date: 10.1.2020
# Enhancements
######################

######################
# Exit Codes
# 0 - Sucess
# 1 - Failed: Admin account credentials are not correct
# 2 - Failed: Mac not domain bound, or otherwise cannot talk to the domain controller
# 3 - Failed: User account to be cached not found in Active Directory
# 4 - Failed: Mobile account failed to cache correctly
###################### 

echo "Begin script"

######################
# Gather and verify admin account
######################

#*------------------------ STRING DECRYPTION ------------------------*#

function DecryptString() {
    echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}"
}

Pass="encryption_nonsense"
Salt="encryption_nonsense"
DecryptString=$(DecryptString "$5" "$Salt" "$Pass") #Decrypt string is parameter 5 in the policy
        

adminUser="$4"
adminPass="$DecryptString"

osvers=$(sw_vers -productVersion | awk -F. '{print $2}')
check4AD=`/usr/bin/dscl localhost -list . | grep "Active Directory"`

## verify that adminuser and pass variables are both passed to the user
if [[ -z "$adminUser" ]] || [[ -z "$adminPass" ]] ; then
    dialog="either Admin User or Password is missing"
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 1
fi

## check the admin password
adminCheck=$(/usr/bin/dscl /Local/Default -authonly "$adminUser" "$adminPass")
if [[ -z "$adminCheck" ]] ; then
    echo "Admin password is verified"
else
    echo "Admin Password not working"
    exit 1
fi


######################
# Check for domain bind, fails out of not bound
######################

echo "Checking for Domain Bind"


if [ "${check4AD}" != "Active Directory" ]; then
    dialog="This machine is not bound to Active Directory.\nPlease bind to AD first. "
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 2
fi

######################
#Popups asking for user to ender userID and Password
######################

echo "Prompting for userToAdd credentials."

## Prompt for Username
userToAdd=$(/usr/bin/osascript<<END
tell application "System Events"
activate
set the answer to text returned of (display dialog "Enter the username to add to this computer" default answer "" buttons {"Continue"} default button 1)
end tell
END
)

## Prompt for Password
userPass=$(/usr/bin/osascript<<END
tell application "System Events"
activate
set the answer to text returned of (display dialog "Please enter the user's account Password:" default answer "" with hidden answer buttons {"Continue"} default button 1)
end tell
END
)

loopCount=0
while [ "$loopCount" -lt 3 ]; do
    # Refresh Directory Services
    if [[ ${osvers} -ge 7 ]]; then
        /usr/bin/killall opendirectoryd
    else
        /usr/bin/killall DirectoryService
    fi
    sleep 15

    ## try to auth the user in advance. this seems to increase the success of the ID command.
    /usr/bin/dscl /Search -authonly "$userToAdd" "$userPass"

    adCheck=`id $userToAdd`
    echo "AD Check is: $adCheck"
    if [[ -z "$adCheck" ]] ; then
        ((loopCount++))
    else
        echo "AD Check successful"
        break
    fi
done 

######################
# Fails script if user account is not found on domain controller
######################

if [[ -z "$adCheck" ]] ; then
    dialog="AD User Not found. Please contact whoever you want to put in this popup box at line 128"
    echo "$dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 3
fi


sleep 2

######################
# Create the new Mobile Account
######################

## hit AD create the user
/System/Library/CoreServices/ManagedClient.app/Contents/Resources/createmobileaccount -a "$adminUser" -U "$adminPass" -n "$userToAdd"

sleep 2

## check to see if the user account was added
userCheck=$(dscl . list /Users | grep "$userToAdd")

if [[ -z "$userCheck" ]] ; then
    dialog="AD User failed to add."
    echo "dialog"
    cmd="Tell app \"System Events\" to display dialog \"$dialog\""
    /usr/bin/osascript -e "$cmd"
    exit 4
fi

## this should query AD to cache the user including the password
dscacheutil -q user -a name "$userToAdd"

## this will auth the user to AD and should also cache their password locally
/usr/bin/dscl /Search -authonly "$userToAdd" "$userPass"

## this kills the menubar so that the fast user switching list refreshes
killall -KILL SystemUIServer

echo "Creating mobile account for $userToAdd."

######################
# Remove FV Access if existing
###################### 

sleep 5
sudo fdesetup remove -user $userToAdd

######################
# Provision Admin Access
###################### 

dscl . -append /groups/admin GroupMembership $userToAdd
echo "Provisioning admin access for $userToAdd."

######################
# Provision FileVault Access
###################### 

sleep 5
## Get the user to be added to FV
userName=$userToAdd

echo "Adding $userToAdd to FileVault 2 list."

## This "expect" block will populate answers for the fdesetup prompts that normally occur while hiding them from output
expect -c "
log_user 0
spawn fdesetup add -usertoadd $userName
expect \"Enter the primary user name:\"
send ${adminUser}\r
expect \"Enter the password for the user '$adminUser':\"
send ${adminPass}\r
expect \"Enter the password for the added user '$userName':\"
send ${userPass}\r
log_user 1
expect eof
"

echo "${userName} has been added to the FileVault 2 list."

######################
# Runs JAMF Recon to assign Mac to $userToAdd
###################### 

jamf recon -endUsername $userToAdd

######################
# Clean up and reboot
###################### 

sudo jamf recon

sleep 30

echo "triggering reboot policy, mac will reboot in 1 minute"

sudo jamf policy -event Force_Restart_Delay

echo "Script completed"

exit 0

 

 

 

emptypony61
New Contributor III

Does the primary FileVault User have to do this after ever reboot? 

scottb
Honored Contributor

I agree with @AJPinto - Macs should be erased/installed between users IMO.

I'm not fluent in HIPAA, but it's a best practice regardless...