SUMMARY
To plug holes left by Addigy’s default user deployment mechanisms, we assembled a script that can deploy new user accounts to a Mac or change their passwords while retaining existing SecureTokens or fetching them from an existing, known admin account.
1 – Introduction
2 – Variables
3 – Tools
4 – Logic
This article was originally authored and posted by Ross Matsuda at sudoade.com.
1 – INTRODUCTION
Managing a Mac fleet often necessitates deploying an administrator account to your devices. Your help desk may leverage this account for endpoint support, or automation scripts may use it to maintain and enhance your workstations. Keeping this account secure is of paramount importance. To prevent employees or external actors from compromising your Macs you must be able to rotate this password, quickly and securely, to a new value at a moment’s notice.
Addigy offers a native tool to deploy user accounts to Macs via their legacy Profiles (MCX) interface. You type in the desired username, full name, and password, check a box if it should be an administrator, and then you can scope that user to any of your devices or policies. A significant downside to this method of user deployment comes into focus if you need to change the password for an account deployed in this manner. If you change the value for the password and save it, Addigy will push it out immediately to any policies or devices you had scoped.
If those admin accounts were ever given a Secure Token (or “volume ownership” status on Apple silicon computers), that will be lost during this sort of password change. Updating a user password this way will alter the user account’s login password but not update the local keychain (generally not a problem for these deployed admin accounts) or the FileVault user database. Once the FileVault password and login password are out of sync, the user account is left in an awkward state — it still believes it has a SecureToken, but the password used to decrypt the disk is now different than the one it expects, rendering it unusable.
With these limitations in mind, I created a more robust method of deploying user accounts leveraging Addigy’s existing tools. The script’s workflow looks like this:
- Check to see if the account we want to build exists.
- Once created, see if there’s another existing admin to pass it a SecureToken.
- If the account already exists, check the password to see if it needs to be updated.
- If the password needs to be updated, change it, retaining an existing SecureToken if present.
2 – VARIABLES
To get started, let’s walk through the variables required for success:
targetAccount="acme_admin"
Short name of the account to deploy, please note field cannot be blank.
targetAccountFullName="Acme Admin"
Full name of the account to deploy.
targetAccountIsAdmin="true"
Should the target account be an admin? Set to “false” if not.
targetAccountPW="My desired password, not in plaintext"
Desired password for the account.
targetAccountOldPW="My previous password, not in plaintext"
The last known password for the deployed account. This is needed to try and retain SecureToken if the account already exists on workstations and the password is being rotated. If no previous password is known, leave this blank.
knownAdmin="theOtherAdmin"
Short name of an existing, deployed account to pass a SecureToken to target account.
knownAdminPW="This other account's password, not in plaintext"
Password for knownAdmin
The notable differentiators here are the inclusion of credentials for a known account to check and an “old password” variable for us to compare against if a password needs to be rotated from a deprecated value to a new one. By providing a known admin account, we can test it for a SecureToken and funnel that to our new or updated account. If there isn’t a known account to leverage, as long as we have the last working password for the already-deployed account, we can use that password to ensure the existing SecureToken is retained.
Important note: only use this workflow if you have some method of making sure the password you’re going to pass is obfuscated from logs and prying eyes. Never transmit sensitive passwords as plaintext to your workstations.
3 – TOOLS
Next, let’s walk through our tools and how they’re used to create a more effortless user deployment and password updating workflow. Keep an eye out for the variables we defined above.
data_key_client
For this to work, we need a method of getting a password safely to a computer without it being displayed or recorded in plaintext. Addigy recommends the online tool Vault (https://www.vaultproject.io) to handle this, and while we haven’t gone through such a deployment, it looks like a spectacular solution. Instead of relying on an external tool, we discovered how to use an existing Addigy binary to handle the same functions.
The data_key_client
is responsible for taking a secret you enter into Addigy (here, a password), assigning it a random alphanumeric fileIdentifier
value, and then translating requests for that fileIdentifier
into plaintext when called. What’s more, these fileIdentifier
values are local to your Addigy instance — no other Addigy customer will be able to decode your secrets; they can only be used within your environment.
As of October 2021, there is no GUI for passing values to the data_key_client
, so we had to get creative to feed values to our automation scripts. I’ll be covering this in more detail in another article. For today, what matters is that we have a mechanism to send passwords to endpoints in a secure way.
user-manager
Next up is Addigy’s user-manager
, which they leverage for their existing account creation methods. This binary has a lot of great functionality, including built-in logic for identifying if an account already exists or not and comparing passwords. You can even encode passwords directly in this tool to be called on other workstations.
One fundamental limitation of user-manager
is that it currently doesn’t have any method to unscramble an encoded password and output it for use in a script or custom software. This limits the versatility of any passwords stored in user-manager
, so we won’t be using those functions in our script.
Create a user account
/Library/Addigy/user-manager -create -username="$targetAccount" -full-name="$targetAccountFullName" -password="$targetAccountPW" -admin="$targetAccountIsAdmin"
Update an existing password (does not retain SecureToken)
/Library/Addigy/user-manager -update-password -username="$targetAccount" -password="$targetAccountPW"
In this workflow, we take advantage of user-manager
to handle account creation, including setting administrator status and updating a password if we know the SecureToken cannot be retained.
fdesetup
This is Apple’s tool for managing FileVault disk encryption. It can perform a wide variety of actions, but we only really care about two: asking for a list of users with SecureToken and stripping a SecureToken from a given account.
See if a user has a SecureToken
sudo fdesetup list 2> /dev/null | grep "$targetAccount"
Remove an existing user’s SecureToken
sudo fdesetup remove -user "$targetAccount"
dscl
We’ll be using dscl
to learn about and act upon local user accounts on a managed Mac. Like fdesetup
, we’re looking at a fraction of what it can do: getting a list of user accounts on a Mac and seeing if we have working passwords for those accounts.
Search for a user account
sudo dscl . list /Users | grep -q "$targetAccount"
Test a password against an account
sudo dscl . authonly “$targetAccount” "$targetAccountPW"
sysadminctl
The reason any of this workflow is possible is due to the features of sysadminctl
. Using it, we can pass SecureTokens from one account to another or non-destructively change a user password while retaining an existing SecureToken.
Pass a SecureToken from one user to another
sysadminctl -adminUser $knownAdmin -adminPassword "$knownAdminPW" -secureTokenOn $targetAccount -password "$targetAccountPW"
Update a password for an account while retaining an existing SecureToken
sysadminctl -adminUser "$targetAccount" -adminPassword "$targetAccountOldPW" -resetPasswordFor "$targetAccount" -oldPassword "$targetAccountOldPW" -newPassword "$targetAccountPW"
4 – LOGIC
With everything defined, we can examine the logic of putting the pieces into a functioning user deployment and password rotation workflow.
Collect Data
Before acting on anything, we populate a series of booleans to decide what action will be taken on the computer:
targetAccountExists=""
Set to true if we get a return from a dscl
check to see if the account exists.
targetAccountHasToken=""
Set to true if the account is part of an fdesetup list
check for accounts with a SecureToken.
targetAccountHasWorkingPassword=""
Set to true if we get a passing result from checking the user account and provided password using dscl authonly
.
targetAccountHasWorkingPasswordOld=""
Set to true if we get a passing result from checking the user account and provided old password using dscl authonly
.
knownAdminExists=""
Set to true if we get a return from a dscl
check to see if a known admin account exists. Include a manual override of this value if it is not provided (if you don’t have a known admin account you want to leverage, set the value to something other than null to avoid errors).
knownAdminHasToken=""
Set to true if the known admin account is part of an fdesetup list
check for accounts with a SecureToken.
knownAdminHasWorkingPassword=""
Set to true if we get a passing result from checking the known admin account and provided password using dscl authonly
.
Act based on results
Now that we have all the information we need, we act based on different combinations of variables.
- If the account doesn’t exist yet,
user-manager
will create it. - If we have a working known admin account with a verified password and SecureToken, pass it to the new account.
- If the account already exists, see if the password is valid or not.
- If the password is valid, make sure the account has a SecureToken. If it doesn’t, but the known admin is detected with a verified password and SecureToken, pass the account a SecureToken before exiting the script.
- If the password is invalid and we have a known admin with a verified password and SecureToken, strip the existing token from the account, change the password, then pass the SecureToken back from the known admin.
- If the password is invalid and we have an old working password, use
sysadminctl
to non-destructively rotate the password using both new and old passwords. - If the password is invalid and we have no method to retain a SecureToken, remove the SecureToken via
fdesetup remove
before changing the password.
By using these data collection tools and logic gates, we can retain SecureTokens while updating passwords, safely expunge SecureTokens that get corrupted during other password rotation workflows, and deploy new admin accounts while taking advantage of existing ones.
Previously, we used a lengthy process that ran over a week (or two) and looked like this: halt further deployment of the admin account, deploy a new admin account with a new password, deploy a custom software to move SecureToken from one admin to the other, deploy a custom software to delete the old admin account.
With our new workflow, it’s a single script that we can update when required and walk away from, confident in knowing it will do everything possible to retain SecureToken.
Comments
0 comments
Please sign in to leave a comment.