Bottom line — you risk exposing credentials any time…
- You put them on a user’s device in any form that’s readable by any automation running there.
- You send them out over a network connection from a client — clients can look at their own network traffic and/or re-direct it anywhere they want.
Consider this in the case of the apps or scripts you put on user devices and that interact with Jamf Pro APIs. Even if you limit the API user’s permissions or don’t think your users are hacker types, you’re taking a risk. Sooner or later one of your devices is going to be compromised and if there are any API credentials there, they’re sitting ducks. If you don’t agree, listen to the Jamf session at this year’s BlackHat and to the researchers’ follow-up visit to the Mac Admins podcast to hear them rail against this practice.
Direct connections to the Jamf Pro API are intended for automation scripts and apps running on secure administrator devices or process automation servers. They are not for client-side scripts or apps.
But there are tons of use cases where you need something on a user device to interact with Jamf Pro’s API. How can you do it and still stay safe?
Approach Number 1: On Macs, Let Recon Do It
Suppose we want to set an extension attribute from a user device.
Maybe you would like to ask students what grade and school they attend and write those to extension attributes. Or maybe you will ask your users some other questions and use the data to add them to some groups you’ll use to scope policies and profiles. You might be tempted to present the users with a form to ask for the information and then write it up to Jamf Pro extension attributes using the API.
Or, maybe we want to set an extension attribute when some event you’re interested in happens. You could create a launch daemon that’s triggered by some event or it could run a script periodically to look for the existence of some condition. The script could use the API to record its findings to an extension attribute in Jamf so you can create an automated response based on values there.
There are so many inventive applications for these techniques, and they’re really good… except for the direct API call part. Jamf Pro’s ability to limit the permissions of an API user is better than most MDMs, many of which don’t offer a way to limit the permissions of an API user at all. But even in Jamf, you can’t say something like, “Give this API user permissions only to update the value for this specific extension attribute and only on their own computer”. If you give an API user write access to computers, it can write anything to any computer, and you’ll probably be giving it write to users and extension attributes too since a computer payload has to interact with those areas as well. Same for mobile devices.
See if you can find a way to get rid of that API call. For the first example, save the form data to a file and then the device can run a recon on itself. Create an extension attribute script that will check if the form’s data file exists and record the values into the extension attribute value. No API calls needed. For the event detection launch daemon example, just have it call a jamf recon instead of writing to the API and replace whatever the API script was going to do with an extension attribute script.
Yes, the above method does incurr the overhead of an entire recon when we could have just updated a specific extension attribute using the API. I live with it. In today’s world, we will sometimes have to sacrifice some convenience and efficiency for security. Not always… as IT admins we evaluate trade-offs like these every day. But in this case, the sacrifice seems worth it.
Example: Infosec caught on that a user that handles customer data was running an unapproved cloud file sync utility and lost their ever-loving minds. They wanted a security incident raised immediately if someone had that running. They didn’t want to wait for a weekly recon to catch it. So, they make a launchd job to look for that process name in the ps process list. If it’s there, call a recon, make an extension attribute that runs the same ps+grep as the detection or add process listings in Jamf Pro inventory prefs, create a smart group to look for that, then have that send an email on group membership change over to the infosec help desk.
Approach Number 2: Find Another Way
I recently read a blog post by another Jamf admin that presents a very clever way to figure out when a user has done an Erase all Contents and settings but did not subsequently re-enroll the device in Jamf. He installs a launchd job that looks for a file that gets created when the erase is about to happen then calls a script to do a Jamf Pro API call to set an extension attribute on the device. How can we avoid putting API credentials on the client if the computer is about to reboot and there’s not going to be time for a recon?
- Block the Reset Content and Settings feature with the restrictions profile by default.
- Put a script on a Self Service item called “Enable Reset All Content and Settings” that
- Runs a script that creates a flag file in some pre-determined path
- Runs a recon
- Create a script extension attribute called “Reset Enabled” that looks for the file created in step 2 and, if it’s there, sets the EA value to the current datetime.
- When the user clicks the Self Service button the flag file is created and the recon runs, setting the current date into the extension attribute.
- Behind the scenes, the smart group that scopes the restriction profile blocking Reset Content and Settings is set up to exclude devices that have a value in that EA value so when the recon runs and the EA value is set, the device falls out of scope for the restriction and profile is removed from the computer.
- Set a post-policy user message to say “You can now reset contents and settings…”
- Set Jamf’s enrollment options to clear extension attribute values on re-enrollment
- The user can reset all contents and settings.
- If they re-enroll, the “Reset Enabled” EA gets cleared out so the default restrictions profile gets applied.
- You can have a smart group to catch any device that has a “Reset Enabled” date greater than one day ago and put an email notification on it so you’ll know to follow up and ask the user why they did not re-enroll in management after they reset their computer.
I know, I know, that is a ridiculously convoluted workflow and not as fast since the user will need to wait for the recon to finish, which isn’t ideal. But it can all be implemented right in the Jamf Pro console — no special launchd jobs or special software installs needed. No API credentials ever go anywhere near the client. The solution is for a particular case but the general ideas have lots of applications.
Approach Number 3: Let a Proxy Do It.
Sometimes there’s just no way around it… you really do need a client-side app or process to read or write Jamf Pro data.
Maybe instead of sending an API call directly to Jamf from the user device, you could instead have the device talk to a web service and let that deal with Jamf Pro? You’d still give the app some credential to present to the web service so it’s not open to the world. For sure, that credential could be skimmed just like actual Jamf Pro API credentials, but there’s a big advantage… you control the API code. That means you can ensure that the API credentials will only be used for the limited purposes you intend. For example, a web service could expose an endpoint used to update the value of a single extension attribute even though the user it uses to talk to Jamf Pro has the rights to update any extension attribute or computer value.
The web service gets the HTTP message from the client and only the web service has the API credentials and does the Jamf API calls. You could host the applet yourself if you have access to a web server, but these are increasingly done in the cloud with something like an AWS Lambda or Google functions. Azure has a bunch of options for doing this kind of thing. They are super cheap, have all the added security that these services offer, and there are options to secure the API credentials in things like AWS Configuration Manager. There are also dozens of cloud services that are purpose-built for creating these kinds of process flows (Zapier, etc.). Some of them are ridiculously easy, like to the point that you can create something really complex with drag-and-drop programming.
In the case discussed in approach 2, instead of having the client-side script send a curl to the jamf api, you could instead hit a “/set_ea/eacs_done/<computer_id>” endpoint and have the web app pick off the computer id and write the current date to the device’s Jamf Pro record.
Of course if you are using a commercial cloud service, this approach is trading one risk for another. We are no longer putting API credentials on the client Macs, but we are giving them to a third party. Make sure the provider you choose has security and privacy certifications consistent with your organizations compliance standards.
Approach Number 4: Same as above, but with per-device Authentication
In the “update values of a single extension attribute” example we’ve been using, a web app might just be setting an informational EA with nothing scoped to it and not used in any other way. So there’d be little point in an attacker trying to mess with it. You might be happy to settle for giving clients a shared credential to use when hitting the web service so you won’t get stuck paying the bill for blind DDOS attacks.
But what if you need the device to prove that it’s really the device it claims to be? There are lots of ways, some simpler than others. For user-interactive flows, you can put up some GUI or a web page and make the user authenticate. You could install a cert on devices that it presents to a web service when negotiating mTLS. Or, you can run a script that:
- Generates a signing certificate
- Generates some random padding bytes
- Loops through every device in Jamf Pro
- Concatenates the device’s id with the padding
- Calculates a signature for those bytes using the signing cert and hashes the result
- Writes that hash to an extension attribute on the device record or saves it on the device, like in a keychain
Then you can use that EA as a value in any Jamf Pro script where the device is going to have to prove itself to something. Or you can include the EA value in an app config for an iOS app.
You would give that same signing certificate and padding secret to your web app. When it gets a call to “/set_ea/eacs_done/<computer_id>/<my_signature_ea>”, it uses the cert and padding to figure out what the signature should be if the the device really were <computer_id>. If that matches the signature presented by the client, the web app processes the request. Otherwise, it discards it.
Yes, an attacker can obtain this signature value off of a device. But as long as the signing certificate is kept safe, they’re going to have a very hard time figuring out the signature for any other client so they’re not going to have a way to mess with the data of any other device record.
4 thoughts on “Stop putting Jamf Pro API Credentials on Clients”
Thanks for the feedback.
Please have a look on my idea of the Jamf Pro EA Proxy.
The problem you’re trying to solve is a great example of how helpful it would be if MDM’s provided a safe/fast/easy way for devices to update their own device record.