Script To Assign a Secure Token and Add User to FV2

dennisnardi
Contributor

I've been struggling a bit making sense and creating a workflow around the secure token change and user creation/FV2 enabling. The workflow in my environment traditionally uses a script using dscl commands or the createuser.py to create our local admin account. The local admin account initiates FV2, then AD users log in and we enable them for FV2. This workflow is now extinct.

I'm creating a dummy account on the computer, then on enrollment a script runs using the sysadminctl commands to create my local account which prompts for gui authentication from my dummy account. This grants my local admin account a secure token and the ability to manage FV2. I can then delete the dummy account from the machine.

I created another script to Self Service that assigns a secure token to a user and enables them to unlock FV2 utilizing some user input.

I'm a very much a novice at bash, so my scripts are probably not the best, but with very little posted about secure tokens I figured I'd share and maybe help a few folks.

The script below creates my local admin account and enables it for ARD. A gui authentication window will pop up and you will have to enter the credentials of a user with a secure token for this to work correctly.

#!/bin/bash

#Created by Dennis Nardi on 2/20/18

acct='YOUR LOCAL ADMIN'
acctname='LONG NAME OF LOCAL ADMIN'
pass=$LOCAL ADMIN PASSWORD
pict="/PATH/TO/PICTURE.EXT"

    # Create Admin Account
    sudo sysadminctl interactive -addUser $acct -password $pass -admin -UID 81 -fullName "$acctname" -hint love -picture "$pict"
if [[ $createacct == *error* ]]; then
    echo "Error: Unable to create $acct account: $createacct"
    exit 1
else
    /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -users $acct -access -on -privs -all -allowAccessFor -specifiedUsers -users $acct -clientopts -setmenuextra -menuextra yes -quiet
    printf "$createacct Done! Created $acctname in /Users/$acct.
"
fi

exit

The script below is what I have in Self Service and run to give a user a secure token and enable them for FV2. I have to do this because AD users are not automatically given secure tokens. I intended to make this script only runnable from my local admin account, but it appears to work under any account as long as your local admin account with a secure token credentials are entered when gui authentication is required. This script pops up and prompts for the username and password.

#!/bin/sh

#  Enable User For FileVault.sh
#  
#
#  Created by Dennis Nardi on 2/20/18.
#  

curUser=$(/usr/bin/stat -f%Su /dev/console)
curPass=$password for admin account with secure token

## Get the desired user's account
echo "Prompting ${curUser} for the desired user to enable for FV2."
Newuser="$(/usr/bin/osascript -e 'Tell current application to display dialog "Please enter the desired user to enable for FV2:" default answer "" with title "Window Title" with text buttons {"Ok"} default button 1 ' -e 'text returned of result')"


## Get the desired user's password
echo "Prompting ${curUser} for the password for desired user to enable for FV2."
NewuserPass="$(/usr/bin/osascript -e 'Tell current application to display dialog "Please enter the password for the desired user:" default answer "" with title "Window Title" with text buttons {"Ok"} default button 1 with hidden answer' -e 'text returned of result')"


## Sets new user with a secure token so it can be enabled for FV2. This requires GUI authentication from the local account but can be run from any account as if secure token admin credentials are entered
sudo sysadminctl interactive -secureTokenOn $Newuser -password $NewuserPass

## 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 $Newuser
expect "Enter the user name:"
send "${curUser}"
expect "Enter the password for user '${curUser}':"
send "${curPass}"
expect "Enter the password for the added user '$Newuser':"
send "${NewuserPass}"
log_user 1
expect eof
"
25 REPLIES 25

alexjdale
Valued Contributor III

I've done something similar, creating a "tokenholder" account used only for escrowing a secure token.

Does your code work? I see what I think are some problems with it.

marlink
New Contributor III

Have any of you been able to get something like this to work when you image a computer (i.e., when there are no GUI-created users on the computer)? While we are using DEP and enrollment-triggered policies to setup NEW computers, we still use imaging for our labs/classrooms and for recovered computers. In those situations, we end up with only one local admin on the computer without a secure token. As a result, we have no way of creating another admin that DOES have a secure token. Apple's suggestion of removing .AppleSetupDone just ends up creating another admin with no secure token -- plus we'd really like to automate the process as much as possible due to the sheer number of computers we're dealing with when re-imaging a classroom. Any suggestions?

apizz
Valued Contributor

@marlink Here's the script we've come up with for our workflow. We are using an OS image restore and DEP workflow for wiping and restoring a fresh copy of High Sierra (after the machine has been upgraded to this same version via the macOS installer app to get the necessary firmware).

This script requires & assumes a few things:
1. You have a set admin account you create as part of the Setup Assistant Process with the same username and password across your machines
2. You have a local admin user (hidden or not) already installed which will receive the secure token and be your new "keeper of the token" as it were w/ it's own same username and password across your machines
3. You have a separate policy configured that will delete the admin account created as part of the Setup Assistant process via custom policy trigger only when the secure token has been successfully given to your local admin user.

Because you are using a known password for your local admin user and providing it via this script, you'll want to use the accounts payload (we have it part as part of the same enrollment policy as this script) that will change the password of this account to something secure after the token as been given.

#!/bin/bash

SECURE_TOKEN_USER="test"
SECURE_TOKEN_USER_PASS="test"
NEW_SECURE_TOKEN_USER="admin"
NEW_SECURE_TOKEN_USER_PASS="defaultadminpassword"

# Give local admin user secure token using admin user credentials established as part of Setup Assistant 
/usr/sbin/sysadminctl -adminUser "$SECURE_TOKEN_USER" -adminPassword "$SECURE_TOKEN_USER_PASS" -secureTokenOn "$NEW_SECURE_TOKEN_USER" -password "$NEW_SECURE_TOKEN_USER_PASS"
exitresult=$(/bin/echo $?)

if [ "$exitresult" = 0 ]; then
    /bin/echo "Successfully added secure Token to ${NEW_SECURE_TOKEN_USER}!"
    # With token successfully added to local admin, delete temp DEP admin user
    /bin/echo "Deleting temp ${SECURE_TOKEN_USER} DEP admin user ..."

    jamf policy -event deletedeptestuser
    exitcode=0
else
    /bin/echo "Failed to add secure Token to ${NEW_SECURE_TOKEN_USER}."
    exitcode=1
fi

exit $exitcode

lynnaj
New Contributor III

Thank you for posting this. It's going to help me out of a jam with over 150 computers that now have the secureToken assigned to the wrong user!

apizz
Valued Contributor

@marlink If you don't have an admin account on the machines with a secure token, I don't think there's a way to get it. You'd have to wipe the machines and reinstall the OS and go through the Setup Assistant in order to get a secure token and (optionally) give it to additional users.

burdett
Contributor II

@aporlebeke if you delete your "admin" account, then you can create a new "admin" account with a valid secure token by running the setup wizard again. To do this, run the following command in Terminal:
sudo rm /var/db/.AppleSetupDone
then reboot and go through the system setup, and you will be prompted to create an account, this will be an admin account with a secure token.

PaulHazelden
Valued Contributor

I was curious, so I tried...
sudo sysadminctl interactive -secureTokenOn $Newuser -password $NewuserPass
Mac running 10.14.2, and there were no accounts with a secure token. Its a fresh build, and the hidden admin account is put in place with a script.

In Terminal I put in the command, and it wanted my admin password, as per usual for sudo.
Then it launched a GUI window asking for the admin password again, which I did.
And now the admin account has a secure token.

Not worked out yet how to script around the GUI box. But as my main script is running as root, it may not be a problem.

sshort
Valued Contributor

@PaulHazelden yeah there were some changes in 10.14.2 that @frederick.abeloos was kind enough to post about

alexjdale
Valued Contributor III

Wow, nice change in 10.14.2! I was just the other day giving our Apple SE the business because the kept talking about how secure the Secure Token paradigm is, but I counter with "It's a lot less secure if we can't encrypt a device at all."

Edit: Well, I just tested this on 10.14.2 with a mobile admin account that has no Secure Token, and applied a FV policy from Jamf. Same problem: the OS knows it needs to enable FV on next login, it prompts for it and logs me in without an error message, but after I logged in FV was not enabled and my Secure Token status was still DISABLED. Not sure what I am missing.

Edit 2: Ok, I see what's happening, their solution was to fiercely protect the first user account you create. I created a local admin account during setup, and it simply will not let me take ANY action that would break that account's secure token. I cannot delete the account under any circumstances, not can I use passwd to force a password change, it requires the old password. So this is basically useless for the use case I was hoping for, and still relies on that account and its credentials to do anything, which is what I want to avoid.

TTG
Contributor
Contributor

@alexjdale 10.14.2 works fine with Jamf Pro policies and mobile accounts.

In case you do have a local account with a token macOS will inform any mobile account without a tonen that they need on. For which an Admin token holder intervention is needed.

But if no token holder exists machine, a FV policy or profile will just work fine. The mobile account will get the token on enabling FV at next login.

The issue your are seeing, FV not being enabled even if the popup is there, was an issue in 10.14.1 and is fixed in 10.14.2

If you are still seeing similar issues there is something else going on. Probably there is another user with an account and you try to force FV for this mobile account before it gets a secure token or before macos could prompt the warning.

blog

I also made a script to possible workflow issue: script

When using Mobile Accounts I would avoid logging in with any local account. Just let the mobile account log in as first account (bind at prestage or enrollment complete policy) and sort out your tokens before enabling FileVault. Policy will work without any problem and the account enabling FV gets a token.

But in all other cases the script above allows to manipulate the tokens and fix stuff.

TTG
Contributor
Contributor

Apart from that macOS will indeed try to make it hard to delete the only Secure Token holder. Not the first user as such. Just the account which is the only Token Holder.

jameson
Contributor II

Does there exist a script that runs on a computer to determine which user has the secure token (without having to run script on each logged in user)

TTG
Contributor
Contributor

@jameson Diskutil apfs listcryptousers /

This command will give you all UUIDs for token holders on the machine.

But the idea of the script above is not per se ‘to run it for every user to find out who has the token’, the fact that it checks for a logged in user is that whatever you want to do..: you will need the logged in users password.

Unless NO token holder exists on the machine, but that’s part of the script too.

stutz
Contributor

@frederick.abeloos

Admin1 - Currently used to hand out SecureTokens.
Admin2 - Not currently used to hand out SecureTokens.

I want to use Admin2 to hand out SecureTokens to new users and delete Admin1 account from the computer.

After looking at your script I just wanted to confirm that you would use this command to change the "SecureToken enabled administrator" (the account that hands out SecureTokens) to a different user?

sysadminctl -adminUser Admin1 -adminPassword Admin1Password -secureTokenOn Admin2 -password Admin2Password

TTG
Contributor
Contributor

hi @stutz

Yes this would give admin2 a token, provided it does not have one yet.

If admin2 is really an admin it would then be able to give tokens to other accounts.

If all is good, Admin1 can be deleted.

bcrockett
Contributor III

dennisnardi,

I think your script is good for folks like myself that are not expert scripters and want a simple GUI method for tokenizing mobile user accounts. I have been testing your scrip on macOS 10.15.2 Catalina with positive results.

Furthermore, your script allows users to execute actions that are similar to these commands:

  1. The first allows one to see the token status of a user account. The second allows one to add a token. Both are good for testing FileVault scripting workflows.
    sysadminctl interactive -secureTokenStatus username
sysadminctl -secureTokenOn username_which_needs_secure_token_goes_here -password password_goes_here

Which I learn about from this blog post from derflounder 2018.

Additionally, I did a search and review for secure tokens for FileVault users today below are links to a few of the outliers.

I hope the help folks expedite similar research;

1.https://github.com/TravellingTechGuy/manageSecureTokens/blob/master/manageSecureTokens.sh

2.[https://github.com/Yohan460/Automatic-Secure-Token-Granting-Workflow

](https://github.com/Yohan460/Automatic-Secure-Token-Granting-Workflow)

3.https://github.com/mpanighetti/add-securetoken-to-logged-in-user

#####

If you're just starting your learning of SecureTokens on macOS 10.13 or 10.14. or 10.15 below are links to articles I read this week to learn more about workflows.
1. This one is the easiest to read;https://derflounder.wordpress.com/2018/01/20/secure-token-and-filevault-on-apple-file-system/

  1. This one has good charts:https://travellingtechguy.eu/macos-catalina-secure-tokens-part-1-local-accounts/

  2. More good charts. Good for visual learners: https://travellingtechguy.eu/final-wrap-up-on-secure-tokens/

  3. Pro of concept from TTG: https://travellingtechguy.eu/mojave-10-14-2-and-secure-tokens-it-works/

  4. Info on new as of 2019 bootstrap options in Catalina: https://osxbytes.wordpress.com/2019/09/24/about-macos-catalina-bootstrap-token/

  5. And of course apple official documentation on the subject. https://support.apple.com/guide/deployment-reference-macos/using-securetoken-apdff2cf769b/web

Thanks, ~B

mani2care
Contributor

@aporlebeke this realy great but think about the exixting user shuld be an admin and shuld be an secure token owner then only is possible to enable another user and passing the secure token.
is possible to validate if the user should admin & token owner and if admin & toekn owner pass to creat the new account and enable the secure token.

is there like and script

apizz
Valued Contributor

Very old script. I'm not 100% sure of the details, but Apple has added a mechanism for MDMs to give the secure token to standard user accounts w/o the need to enter admin credentials as part of a script.

mani2care
Contributor

I reviewed bellow link i tought this will be my requirement, let me check and get back to you.

https://github.com/TravellingTechGuy/manageSecureTokens/blob/master/manageSecureTokens.sh

kerouak
Valued Contributor

Here you go:

#!/bin/sh
# This script is intended to be used with JAMF Self Service. It will enable SecureToken for the currently logged in user account
# and either add it to the list of to FileVault enabled users or enable FileVault using a Personal Recovery Key.

# Your policy must include script parameters for a SecureToken enabled administrator username and password. For more information
# on using script parameters, please see https://www.jamf.com/jamf-nation/articles/146/script-parameters.

# v1.2 - added debugging trace messages to confirm progress of script and confirm variables are being correctly passed - by Amos Deane - 13 Sep 2018
# v1.3 - corrected userName1

adminUser="$4"
adminPassword="$5"
userName1="$3"
userName2="$6"

# Uses AppleScript to prompt the currently logged in user for their account password.
userPassword1=$(/usr/bin/osascript <<EOT
tell application "System Events"
activate
display dialog "To Enable Filevault, Please enter your login password to Enable Filevault:" default answer "" buttons {"Continue"} default button 1 with hidden answer
if button returned of result is "Continue" then
set pwd to text returned of result
return pwd
end if
end tell
EOT)


function separationLine {
echo "----------------------------------------------------------------------------------" 
}


# Enables SecureToken for the currently logged in user account.
enableSecureToken() {
separationLine
echo "Enables SecureToken for the currently logged in user account $userName1"
    sudo sysadminctl -adminUser $adminUser -adminPassword $adminPassword -secureTokenOn $userName1 -password $userPassword1
}

# Creates a PLIST containing the necessary administrator and user credentials.
createPlist() {
separationLine
echo "Creating a PLIST containing the necessary administrator and user credentials"
    echo '<?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>Username</key>
    <string>'$adminUser'</string>
    <key>Password</key>
    <string>'$adminPassword'</string>
    <key>AdditionalUsers</key>
    <array>
        <dict>
            <key>Username</key>
            <string>'$userName1'</string>
            <key>Password</key>
            <string>'$userPassword1'</string>
        </dict>
    </array>
    </dict>
    </plist>' > /private/tmp/userToAdd.plist
}

# Adds the currently logged in user to the list of FileVault enabled users.
addUser() {
separationLine
echo "Adding the currently logged in user to the list of FileVault enabled users"
    sudo fdesetup add -i < /private/tmp/userToAdd.plist
}

# Enables FileVault using a Personal Recovery Key.
enableFileVault() {
separationLine
echo "Enabling FileVault using a Personal Recovery Key"
    sudo fdesetup enable -inputplist < /private/tmp/userToAdd.plist
}

# SecureToken enabled users are automatically added to the list of Filevault enabled users when FileVault first is enabled.
# Removes the specified user(s) from the list of FileVault enabled users.
removeUser() {
separationLine
echo "Removing the specified user(s) from the list of FileVault enabled users."
    sudo fdesetup remove -user $adminUser
    sudo fdesetup remove -user $userName2
}

# Update the preboot role volume's subject directory.
updatePreboot() {
separationLine
echo "Updating preboot"
    diskutil apfs updatePreboot /
}

# Deletes the PLIST containing the administrator and user credentials.
cleanUp() {
separationLine
echo "Cleaning up temp files"
    rm /private/tmp/userToAdd.plist
}

#

enableSecureToken
createPlist
if [ "$(sudo fdesetup status | head -1)" == "FileVault is On." ]; then
separationLine
echo "Filevault is on - adding to secure token"
    addUser
else
separationLine
echo "Filevault is off - enabling. Removing user"
    enableFileVault
    removeUser
fi
updatePreboot
cleanUp

kerouak
Valued Contributor

For Catalina, things are getting better..
Have a look at 'Bootstrap Token'

MrRoboto
Contributor III

@kerouak Thanks for this! Using your self service script to enable FV works... SecureToken granted, drive encrypted, individual recovery key gets escrowed. Although under the computer record in Jamf the Disk Encryption Configuration is blank. Since we are using a script and not the disk encryption payload, we do not specify which Disk Encryption Configuration to use. Is this an issue or can it be ignored?
b9a556d00a024afb8d369fa1e50cea81

MrRoboto
Contributor III

I'm using @kerouak's service script to enable FV as we are currently stuck with mobile accounts (will be moving to local soon) and a small percentage of non-dep Macs (no bootstrap token on these). As pictured above the drive encrypts but no disk encryption configuration is selected as we did not use the disk encryption payload in the policy, just used the script in the policy. After testing I can confirm that the IRK gets escrowed and is valid, it works to unlock the drive. I modified the script... added a timeout to the password dialog, verify the password entered by the end user, and prompt/exit if invalid. Also prompt upon success and perform a recon, commented out remove user as we are keeping the admin user at this time.

#!/bin/sh
# This script is intended to be used with JAMF Self Service. It will enable SecureToken for the currently logged in user account
# and either add it to the list of to FileVault enabled users or enable FileVault using a Personal Recovery Key.

# Your policy must include script parameters for a SecureToken enabled administrator username and password. For more information
# on using script parameters, please see https://www.jamf.com/jamf-nation/articles/146/script-parameters.

# v1.2 - added debugging trace messages to confirm progress of script and confirm variables are being correctly passed - by Amos Deane - 13 Sep 2018
# v1.3 - corrected userName1
# v1.3.1 - added password entry timeout, verify user password, invalid prompt, success prompt, recon at end, commented out remove user

adminUser="$4"
adminPassword="$5"
userName1="$3"
userName2="$6"

# Uses AppleScript to prompt the currently logged in user for their account password.
userPassword1=$(/usr/bin/osascript <<EOT
tell application "System Events"
activate
with timeout of 300 seconds
display dialog "Please enter your login password to enable FileVault:" default answer "" buttons {"Continue"} default button 1 with hidden answer
if button returned of result is "Continue" then
set pwd to text returned of result
return pwd
end if
end timeout
end tell
EOT
)

# Validate user password
if ! dscl /Local/Default authonly "${userName1}" "${userPassword1}" ; then
    echo "Invalid user password entered"
    # Display error dialog
    /usr/bin/osascript -e 'Tell application "Self Service" to display dialog "Error enabling FileVault... Invalid user password or timeout." with title "Error" with text buttons {"OK"} default button 1 with icon caution'
    exit 1
fi


function separationLine {
echo "----------------------------------------------------------------------------------" 
}


# Enables SecureToken for the currently logged in user account.
enableSecureToken() {
separationLine
echo "Enables SecureToken for the currently logged in user account $userName1"
    sudo sysadminctl -adminUser $adminUser -adminPassword $adminPassword -secureTokenOn $userName1 -password $userPassword1
}

# Creates a PLIST containing the necessary administrator and user credentials.
createPlist() {
separationLine
echo "Creating a PLIST containing the necessary administrator and user credentials"
    echo '<?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>Username</key>
    <string>'$adminUser'</string>
    <key>Password</key>
    <string>'$adminPassword'</string>
    <key>AdditionalUsers</key>
    <array>
        <dict>
            <key>Username</key>
            <string>'$userName1'</string>
            <key>Password</key>
            <string>'$userPassword1'</string>
        </dict>
    </array>
    </dict>
    </plist>' > /private/tmp/userToAdd.plist
}

# Adds the currently logged in user to the list of FileVault enabled users.
addUser() {
separationLine
echo "Adding the currently logged in user to the list of FileVault enabled users"
    sudo fdesetup add -i < /private/tmp/userToAdd.plist
}

# Enables FileVault using a Personal Recovery Key.
enableFileVault() {
separationLine
echo "Enabling FileVault using a Personal Recovery Key"
    sudo fdesetup enable -inputplist < /private/tmp/userToAdd.plist
}

# SecureToken enabled users are automatically added to the list of Filevault enabled users when FileVault first is enabled.
# Removes the specified user(s) from the list of FileVault enabled users.
removeUser() {
separationLine
echo "Removing the specified user(s) from the list of FileVault enabled users."
    sudo fdesetup remove -user $adminUser
    sudo fdesetup remove -user $userName2
}

# Update the preboot role volume's subject directory.
updatePreboot() {
separationLine
echo "Updating preboot"
    diskutil apfs updatePreboot /
}

# Deletes the PLIST containing the administrator and user credentials.
cleanUp() {
separationLine
echo "Cleaning up temp files"
    rm /private/tmp/userToAdd.plist
}

#

enableSecureToken
createPlist
if [ "$(sudo fdesetup status | head -1)" == "FileVault is On." ]; then
separationLine
echo "Filevault is on - adding to secure token"
    addUser
else
separationLine
echo "Filevault is off - enabling. Removing user"
    enableFileVault
    #removeUser     # We do not want to remove users at this time
fi
updatePreboot
cleanUp

# Display success dialog
/usr/bin/osascript -e 'Tell application "Self Service" to display dialog "FileVault has been enabled..." with title "Success" with text buttons {"OK"} default button 1 with icon note'
# Run Jamf recon
echo "Running Jamf recon"
/usr/local/jamf/bin/jamf recon
exit 0

hbx
New Contributor

I am still new to MDM with scripting and this is my first time seeing scripts containing passwords. My concern is, are they really secure?