Open wakco opened 1 year ago
Not saying no... but this would significantly increase the potential attack surface for whatever Jamf API account is stored by super
in the System Keychain. There is potential to gain an admin password for not just the local computer... but for every computer in an organization.
Thankfully admin access is required to look at that keychain entry, but I agree, that said, the security issues are something Jamf will need to consider regarding their API.
I had written something else, but you have given me an idea for a decent feature request for Jamf, for the jamf command, be able to do something like jamf adminLogin softwareupdate --install --all --restart
, and then the jamf command could on Apple Silicon Macs, automatically add or pass the admin login credentials to softwareupdate, and perhaps the same for sysadminctl as well as any other command we could have it handle. Perhaps adding something from the policy to allow a script to use the command (for sysadminctl).
Jamf "Idea" JN-I-27135
Digging into this new feature... there are a lot of dependencies and several outstanding issues that make it unsuitable for deployment at this time. I will reevaluate as Jamf makes improvements.
@wakco I have been trying on Apple Silicon Macs to get the MDM workflow for upgrades to work, so far unsuccessfully and I am willing to believe this is mainly down to Apple. ☹️
As such I am now considering the possibility of using a local admin account like you appear to be. I have recently joined an organisation and currently they have the same credentials on all Macs which is on my list of things to fix, i.e. to implement LAPS. I have had a brief look at Jamf's new LAPS feature but I get the impression it is not adequate yet to the purpose. I have previously used a script which stores the LAPS password in an extension attribute.
It looks like it might be possible to use the value of this extension attribute as a parameter to a policy script parameter. If so I could then run the Super policy with parameters which pass the LAPS user name and password to Super during deployment and allow super to then create a local account.
According to the Jamf docs, this is done by using a Jamf variable in the script parameter so it might looks something like the following -
--admin-password=$EXTENSIONATTRIBUTE_5
Note: The 5 refers to the number of the extension attribute so will be dependent on your setup.
I have a string suspicion however that the eventually (hopefully) created local credential will lack the required volume owner etc. permissions as Apple for example make it difficult to script giving a secure token to a script created user account. (Another Apple issue.)
You can not set the value of a script parameter via that mechanism. It's only designed for Configuration Profiles.
@Macjutsu The $EXTENSIONATTRIBUTE was to read the LAPS password for the Mac that Jamf was running the policy and script on, not to set it. It would be set by the LAPS script using the Jamf API.
The idea was to read the LAPS password from the extension attribute and pass it to Super. As mentioned I have previously used a LAPS script which stored the password in plain text in the extension attribute and hence should work as is. However these days it is considered better to store the password in an encrypted form even though you have to be an approved Jamf user to access it.
I have had a look at some other LAPS solutions for Jamf and the following one looks better to me.
The author has provided an example script to retrieve and decode the LAPS password. In theory Super could be enhanced to use the $JSSID to look up the relevant extension attribute and decode its contents and then have the correct LAPS password for the Mac in question and hence be able to use it to do the required authentication locally.
The script will need some modifying for this purpose to make it fully automated and hence remove some of the interactive aspects. (Basically strip out everything except reading and decoding the extension attribute.) I could myself build this in to Super but I would have to do this each time you updated it. Ideally Super would be modified to have an additional script parameter e.g.
--use-macOSLAPS and --use-macOSLAPS-off
And accordingly use this or not. It should be possible to put this modified script in the Super folder and call it based on this additional parameter. I could create the modified script for you if you are willing to add it to Super.
I am aware of LAPS solutions... but there are two fundamental security problems with leveraging them in any local process or script like super
.
super
must authenticate to the Jamf Pro API... this means that super
would be storing that credential in the System Keychain. Because of Jamf Pro's security model, that credential, if discovered, would allow anyone to have full read access to your entire Jamf Pro inventory database. Obviously, this would be a significant security risk.@Macjutsu Yes, I am aware of the volume ownership requirement. I have previously had Jamf run a policy which gets the user to authenticate locally giving an IT admin account a secure token if needed and then previously had a Jamf policy which does the LAPS function. (I am looking at a new way which piggybacks on Jamf Connect to automate creating a local admin account with an initial password and secure token/volume ownership.) So I would be ensuring that the local admin account is up to the job for Super and hence can be used by Super to create the Super service account.
I in fact did use this today to get Super to work for doing the upgrade successfully. I passed the password for the local admin account in a Jamf policy with the Super install script, it used the password to create the Super service account, I had also a Jamf Profile setting for Super to failover (ALWAYS) in the event of an MDMworkflow failure and it then successfully installed the macOS Ventura upgrade.
Super is already setup as per your instructions to authenticate to Jamf API - this is working. It can therefore potentially use the same approach to authenticate reading an extension attribute which is storing a LAPS password. I am using the $JSSID approach recommended by your instructions which supposedly means it cannot read the entire inventory. (Although I would have thought it would be possible to emulate this by looping through all possible JSSID values.)
All the LAPS solutions for Jamf rely on using the API to set and to read the values and I would presume this includes Jamf itself. However the one I am referring to like several others does encrypt the contents. This encryption key could be passed to Super via a script parameter.
Yes... but due to the limitations of Jamf Pro's security model... the permissions required for the Jamf Pro API account used by super
to read EAs would also allow it read EVERY SINGLE EA for ALL of your computer records.
And it doesn't matter if you have added the extra (technically not actually more secure "encrypted" strings) method because super
would have to store the decryption secrets in the Keychain as well. In other words, you just made it slightly more complicated but no more secure.
In short, because of limitations in Jamf Pro and because super
has to store the secrets, if someone can view their local System Keychain (default for admin), then they can know all the LAPS passwords for your entire environment.
@Macjutsu I see your point about Super directly reading EA fields.
However I think if we used as a script parameter something like -
--admin-password=$EXTENSIONATTRIBUTE_5
This would not require giving the SuperApi credentials full EA access and the content of the designated EA would be passed to your script. A Jamf admin would like all the other script parameters specifically set this up so it cannot be auto scraped and it would also be for a specific script i.e. Super. Furthermore the content of the EA in this case would be in encrypted form and would need decrypting in your script to utilise. This likely will require using two script parameters one for the encryption key and one for the encrypted admin password as above. Basically the same way that the macOSLAPS script works except your script would not have full EA access.
This is NOT something Jamf Pro can do... As I have already stated earlier....
"You can not set the value of a script parameter via that mechanism. It's only designed for Configuration Profiles."
Again... sending the value of Jamf Pro attributes is only possible with CONFIGURATION PROFILES.
@Macjutsu Ok thanks for the correction. Whilst technically it would be possible to send the encrypted password to the Mac as part of a profile this is not going to be desirable even though it is encrypted.
I will consider writing my own script to retrieve the encrypted password - via the Jamf API, decode it and call super with command line parameters to allow it to create the service account.
Another issue with the configuration profile is that Jamf Pro does not send new configuration profiles when the value of a $VARIABLE changes. So when you cycle the LAPS password you would have to push a new configuration profile manually or with some other external automation.
This last point would be the best approach, as long as you don't store any data on the local drive while the pre-script is running.
What I ended up doing is use the same component that populates the LAPS Extension Attribute in a script to pass local admin creds to super to create the super service account. If for some reason LAPS hasn't been setup on a particular Mac, it pivots to deploying super using the MDM workflow. It's been working out well for me so far.
@Macjutsu @wakco @iDrewbs
Ok, attached is a script that is designed to sit between Jamf Pro and Super. I have as will be obvious when you view it based it on the Super script itself. Here is the main logic.
--admin-acount
starts with 'lapssecret-' then it is presumed to contain the name of an extension attribute that will contain the LAPS password, if an additional new command line parameter of --admin-crypt-key
is defined then this a) indicates that the admin-password is encrypted and b) if the contents of --admin-crypt-key
starts with 'lapscryptkey-' then this indicates the extension attribute containing the decryption key required to decrypt the admin-password value, if both are pointing to extension attributes then the encrypted value is retrieved and then the decryption key is retrieved and it is used to decrypt the admin-passwordThis will allow all the following possible scenarios to work.
--admin-password=xxx
and do not use --admin-crypt-key==xxx
--admin-password=zzz
and the same decryption key in --admin-crypt-key=zzz
--admin-password=lapssecret-zzz
to point to the extension attribute--admin-password=lapssecret-zzz
to point to the extension attribute and define the decryption key via --admin-crypt-key=xxx
--admin-password=lapssecret-zzz
to point to the extension attribute and --admin-crypt-key=lapscryptkey-zzz
to point to the extension attribute containing each decryption keyThis should cover all the following LAPS solutions and possibly others
I have not yet looked closely at it, but I am led to believe that the new Jamf Pro built-in LAPS does not use the existing password to authorise changing to a new password and hence is doing a password reset each time, this would therefore result in destroying the secure token of the LAPS account and makes it unsuitable for my own purposes.
Note: I am sure that my initial script provided here could firstly have a lot more lines removed as un-needed in this case, remember it ends up calling the real, full super script. However as it is the inclusion of such lines may make it easier to see how these changes could if desired be built-in to the Super script itself. Kevin may or may not want to consider that but the reason I did not would be that each time a new version of Super was released the same changes would need to be reapplied. I also suspect that Kevin being far more familiar with his original code may see ways this script could be done more elegantly.
Note to @Macjutsu I am using the $JSSID to retrieve the extension attributes and hence minimising the API permissions needed
This is a good method if your plan is to only give super
the admin password long enough to create it's own service account. Further, since it only needs to be run once, keeping it as a separate "installation" script makes sense as you wouldn't need any of this code in super
itself.
However, how does using $JSSID
limit the permissions for the API account? Perhaps for the execution of this single script it will... but not for the API account itself. If the API account has permissions to read EAs... it can read ALL EAs... even if this particular script only reads a single EA.
In other words, if this script is using the same Jamf Pro API account that is also passed to super
, then a local admin could easily access the Jamf Pro API credentials that super
stores in the System Keychain and then gain access to all of the data stored in every computer's EA value.
I had misunderstood your use of the $JSSID so no, it slightly helps reduce access but not materially.
As it happens, in order to read extension attributes you have to have read access to the computer record(s). The only way I can see to further secure this with respect to your concern would be either -
It should be noted I went to great effort with my initial script design to provide masses of flexibility for LAPS support but only need a single extra parameter field.
Regarding the keychain storing Super API credentials.
I seem to recall seeing some software products create their own dedicated keychain with their own password to that keychain so only their software can access it.
super
could create another keychain, but then where would it store the password for that keychain?
Any "secret" method super
could use would be visible to the public due to its open source nature. As such, we literally couldn't hide access to that second keychain.
This is why most processes use the System Keychain... it's the only keychain that doesn't need a password... just admin/root access.
@Macjutsu For what it's worth, I found an error in the last few lines of my proposed script.
Where I pass the regenerated command line parameters to the real super script this needs changing to the following to ensure the parameters are handled properly.
curl --silent -o /tmp/super -L -O https://github.com/Macjutsu/super/raw/main/super
chmod +x /tmp/super
array=($commandPARAMS)
mycmd=(/tmp/super "${array[@]}")
"${mycmd[@]}"
I can see that my script now successfully launches the real super script. I am not convinced however that Super is doing anything.
It seems to suggest the next scheduled event is right now except nothing ever happens. Super.log attached below.
Ok first log is when I use my script (with above fix) to run the real super script. I seem to get an error 137 returned but as per the log Super does seem to install and run. It however DOES NOT show any dialogs.
Second log is when I use a barebones script with the parameters hard coded in to a variable and then call the real super scrip the same way. This does not seem to trigger an error 137 but again no dialogs are shown after.
On a Mac with a need to do an upgrade IT DID DO IT but with as mentioned no dialogs.
If you post this to your own repository I can link to it in the Wiki.
@Macjutsu I can do that, but I would like to resolve the two queries first.
Fri Jun 16 18:18:45 G4-0001257 super[95723]: **** S.U.P.E.R.M.A.N. 3.0 INSTALLATION ****
Fri Jun 16 18:18:45 G4-0001257 super[95723]: Installation: Copying file: /Library/Management/super/super
Fri Jun 16 18:18:45 G4-0001257 super[95723]: Installation: Creating search path link: /usr/local/bin/super
Fri Jun 16 18:18:45 G4-0001257 super[95723]: Installation: Creating LaunchDaemon helper: /Library/Management/super/super-starter
Fri Jun 16 18:18:45 G4-0001257 super[95723]: Installation: Setting permissions in: /Library/Management/super
Fri Jun 16 18:18:45 G4-0001257 super[95723]: **** S.U.P.E.R.M.A.N. 3.0 STARTUP ****
Fri Jun 16 18:18:46 G4-0001257 super[95723]: Startup: Found previous super instance running with PID 95536, killing...
Error running script: return code was 137.
2. Any idea why the dialogs failed to appear but it did do an upgrade. It was a different Mac that did the upgrade so will not be in the above two logs.
This is a Jamf Pro issue: https://community.jamf.com/t5/jamf-pro/issue-running-script-from-policy/m-p/260226
@Macjutsu Based on that Jamf article are you advising I add code to my script to call the real Super script via the logged in user? Or to create a new fresh policy with my unaltered script?
From the solution, "I was able to fix this by just creating a new policy, copying everything over and deleting the old policy. Something must have become corrupted or sticky in that old one that was causing all the issues. Go figure, had nothing to do with the script."
Sometimes things in Jamf Pro become corrupted and this results in error 137. It's resolved by simply re-uploading the scripts and creating fresh Policies with the same settings.
@Macjutsu Thanks for the help.
I did make a brand new policy, I made a new script entry in Jamf Pro - although I copied and pasted the script itself and tried again, it still gave an error 137. I then made a new policy again, a new script but this time just the portion of my script that calls yours with hard coded $commandPARAMS (see below) - most of the script is building the commandPARAMS.
This stripped down script worked without an error 137 being triggered. :(
Anyway, whilst I myself continue looking in to this, here are two other questions. I am also getting an error as follows.
Mon Jun 19 09:55:39 G4-0001257 super[76778]: Exit: LaunchDaemon com.macjutsu.super.plist is scheduled to start right now.
/Library/LaunchDaemons/com.macjutsu.super.plist: Service is disabled
Bootstrap failed: 119: Service is disabled
Mon Jun 19 09:55:39 G4-0001257 super[76778]: **** S.U.P.E.R.M.A.N. 3.0 EXIT ****
My stripped down script is as follows and whether run locally or via a Jamf Policy event gives the 119 error.
#!/bin/sh
commandPARAMS="--jamf-account=superapi --jamf-password=password --admin-account=ladmin --admin-password=password"
curl --silent -o /tmp/super -L -O https://github.com/Macjutsu/super/raw/main/super
chmod +x /tmp/super
array=($commandPARAMS)
mycmd=(/tmp/super "${array[@]}")
"${mycmd[@]}"
I am also attaching below a copy of the Super.log from a Mac that did successfully run the Super upgrade cycle but did not show any dialogs or prompts. So it 'suddenly' rebooted.
@Macjutsu Regarding the error 137, this appears to have been down to my retaining too much of your script, in particular PID related code, removing that seems to cure the 137 error.
This leave the 119 error, if you can shed some light on what might cause that I will likely need to remove some more parts of the original script to prevent that conflict.
@Macjutsu FYI - I have been working with the author of the macOSLAPS solution I am using and he has added the ability to work with existing LAPS accounts all using the same credentials and then take over randomising them. I have been working on a script to then read those details and pass them to your Super script.
I think this is pretty much working, I am now modifying my script to use a different Jamf API account so Jamf calls my script, my script uses the passed API credentials which are not stored and my script passes the normal SuperAPI credentials and the device specific local admin credentials to your Super script. I think this as much as possible prevents global access to credentials since the SuperAPI credentials cannot read the extension attributes and the API credentials which can are not stored on the client Mac.
I have created my own repo for this although I have not yet finished with testing.
https://github.com/jelockwood/Super-Glue
I have noticed you have recently updated the recommend API settings and added more access to Managed Software Update Plans. It could be that this might help my own experience and why I have felt I needed to enable failover to local admin credentials.
As a separate issue, I do get the impression that even after I have successfully installed your Super script, it only seems to run once and not after a successful macOS update/upgrade run again even if a new version is available. As it happens the way I will update the credentials for Super after the macOSLAPS has changed them should get round this by effectively re-installing and starting Super again. Looking at your launchdaemon I see it is set as launch only once so this might explain matters.
How is Super supposed to automatically re-check for new updates? Does it rely on the user regularly rebooting? What happens if a user does an update which does a reboot, the check sees no other updates and then Super 'quits' and the user does not do another reboot for a number of days? It would appear no further checks happen.
Here is the relevant section from the super.log
Tue Jun 20 14:29:57 G4-0001257 super[54683]: Startup: Found that super is installing, restarting with new LaunchDaemon...
Tue Jun 20 14:29:57 G4-0001257 super[54683]: Verbose Mode: Function makeLaunchDaemonRestartNow: LaunchDaemon com.macjutsu.super.plist:
<?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>Label</key>
<string>com.macjutsu.super</string>
<key>LaunchOnlyOnce</key>
<true/>
<key>AbandonProcessGroup</key>
<true/>
<key>UserName</key>
<string>root</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Management/super/super-starter</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Tue Jun 20 14:29:57 G4-0001257 super[54683]: Exit: LaunchDaemon com.macjutsu.super.plist is scheduled to start right now.
Tue Jun 20 14:29:57 G4-0001257 super[54683]: **** S.U.P.E.R.M.A.N. 3.0 EXIT ****
As you can see it has not re-run Super since June 20th and hence has not spotted and installed macOS 13.4.1 which is now available. I have rebooted it today as well.
As a default super
does not check again after a successful update/upgrade. However, there is an option for this: https://github.com/Macjutsu/super/wiki/Deferral-Behavior#recheck-deferral-timer
@Macjutsu It turns out the way my script was (re)installing Super when updating the local admin credentials passed to Super was resulting in the launchdaemon being unloaded and not reloaded. I modified my script to explicitly handle this and it now reliable re-runs.
I had already defined the deferred-recheck setting with a value so it was not that.
I have a couple more scenarios to test but I feel my script now fully works with the macOSLAPS solution. I am gradually updating my documentation.
Adding the tag of "Help Wanted" to indicate that unless a safer method is found, I won't be working on this.
Change in Jamf Pro 10.46.0 … Local Administrator Password Solution (LAPS) provides API access to look up an existing account and password.
Obviously we would still need to make sure the account has a Secure Token, but that is relatively easy to do, this would enable super to collect a known changing password and use that to trigger softwareupdate.