Removal of Local Admin Rights

MTFIDjamf
Contributor II

Scenario:
When Mac laptops are imaged and joined to the domain in our corporate environment the desktop support teams have the option of giving the domain user local admin rights on their Mac device. Unfortunately, more often than not, they do so.

The Mac image comes with a generic local admin account that all devices have and needs to be kept in place. These rights need to be preserved.

New policy has come from the top, all local admin rights for domain users are to be removed from all domain devices.

I have been recently given the role of Casper Admin with zero experience in Casper or OSX after an engineer left.

Questions:
How do we remove these rights from each device centrally through Casper JSS? Is there a way to do so? If this has to be some sort of script, what exactly should it be and how would it be distributed to devices as they come online and continue to check the devices to ensure that there are no new local rights given?

Any help is definitely appreciated and certainly required...

1 ACCEPTED SOLUTION

mm2270
Legendary Contributor III

Hello @MTurnerFMRCO This seems like one of those topics that pops up quite often around here. Before I go into anything, have you already done some searches here on 'remove admin privileges' or something like that? If not, I encourage you to do that as I'm pretty certain you will get a lot of hits on the topic.

Now, as to what you've been tasked to do, let me make sure I understand. You have a standard local administrator account on all Macs that needs to be in place, but in some cases regular user accounts are being created (accidentally) as admins, not regular users. And you need to find a way to remove these rights from any accounts except the one local admin one, correct?
If so, you would likely need to use a scripted process to loop over all local accounts on the Mac, excluding the one local admin account you know should be there, and then use something like dseditgroup to remove the administrative rights from the accounts.
If this all sounds good, something like the below should be a start. I know I'm duplicating what's already been posted like a 100 times on other threads, but...

#!/bin/bash

localAccts=$(dscl . list /Users UniqueID | awk '$2>500{print $1}' | grep -v localadmin)

while read account; do
    echo "Making sure $account is not in the local admin group"
    dseditgroup -o edit -d $account admin
done < <(echo "$localAccts")

exit

You would need to edit the above by changing the "localadmin" to whatever local administrator name you know should be left as an admin. This will locate only accounts with UIDs higher than 500, so basically everything from UID 501 and up. If an account on the Mac happens to have a lower UID, this script will not locate it, so just keep that in mind. Should go without saying, but obviously test this thoroughly or any other script anyone posts, before implementing it to be sure its not damaging anything.

As for the second issue of detecting unauthorized admin accounts, take a look at this thread.
https://jamfnation.jamfsoftware.com/featureRequest.html?id=2065
There are many possible solutions to this, but on that thread you'll see a few Extension Attributes you can put in your JSS to capture the status of accounts, as in admin or standard, domain or local, etc.

View solution in original post

26 REPLIES 26

ndobric
New Contributor

Replacing the admin.plist with your default will restore the admin group to it's original state, leaving your service account in tact but removing rights from the user.

mm2270
Legendary Contributor III

Hello @MTurnerFMRCO This seems like one of those topics that pops up quite often around here. Before I go into anything, have you already done some searches here on 'remove admin privileges' or something like that? If not, I encourage you to do that as I'm pretty certain you will get a lot of hits on the topic.

Now, as to what you've been tasked to do, let me make sure I understand. You have a standard local administrator account on all Macs that needs to be in place, but in some cases regular user accounts are being created (accidentally) as admins, not regular users. And you need to find a way to remove these rights from any accounts except the one local admin one, correct?
If so, you would likely need to use a scripted process to loop over all local accounts on the Mac, excluding the one local admin account you know should be there, and then use something like dseditgroup to remove the administrative rights from the accounts.
If this all sounds good, something like the below should be a start. I know I'm duplicating what's already been posted like a 100 times on other threads, but...

#!/bin/bash

localAccts=$(dscl . list /Users UniqueID | awk '$2>500{print $1}' | grep -v localadmin)

while read account; do
    echo "Making sure $account is not in the local admin group"
    dseditgroup -o edit -d $account admin
done < <(echo "$localAccts")

exit

You would need to edit the above by changing the "localadmin" to whatever local administrator name you know should be left as an admin. This will locate only accounts with UIDs higher than 500, so basically everything from UID 501 and up. If an account on the Mac happens to have a lower UID, this script will not locate it, so just keep that in mind. Should go without saying, but obviously test this thoroughly or any other script anyone posts, before implementing it to be sure its not damaging anything.

As for the second issue of detecting unauthorized admin accounts, take a look at this thread.
https://jamfnation.jamfsoftware.com/featureRequest.html?id=2065
There are many possible solutions to this, but on that thread you'll see a few Extension Attributes you can put in your JSS to capture the status of accounts, as in admin or standard, domain or local, etc.

Thanks for this, the script works great. However, the linked article is now 404 (probably because the forum software has changed).

Is this what you meant: https://community.jamf.com/t5/jamf-pro/find-unauthorized-admins/td-p/100151 ?

MTFIDjamf
Contributor II

@ndobric

Thanks for the reply. I have searched one of our imaged Macs and do not find an Admin.plist. Any idea where this would/should be?

Dealing with Mountain Lion here.

MTFIDjamf
Contributor II

@mm270

Thank you for the reply.
I have been going through other posts here but did not see anything exactly the same as what we have going on, and there are a bunch to go through.
Your understanding is 100% correct.
I think that I am getting what you are saying and can give it a try. As far as the UID portion, what is the significance of the number 500? Can I not just use '1' and have it go up from there to find everything that is not the one local service admin account that we need to keep as is?

If the script does work, what is the next step? How do I get it packaged and make sure that it runs on each machine at startup?

Apologies for the basic questions here, extremely new to all of this.
Thank you.

mm2270
Legendary Contributor III

Hi @MTurnerFMRCO No, you absolutely don't want to go from UID 1 and up, unless you want to completely bust your Macs. There are tons of OS X specific service accounts located in the sub 501 UID range that should be not altered since the OS requires them for various functions. Some of them will be in the local admin group, by design.
Most user accounts get created from UID 501 and going up from there. Its been this way on OS X since the very first 10.0.0 version. While its not impossible for a user account to get made with a lower than 501 UID, its not typical, especially when talking about Active Directory accounts, since the normal process is to make the user's locally cached account UID match what it is in Active Directory. These are usually much higher, like as in "1524544018" or something like that. Generally speaking 1000 and up UIDs are reserved for directory based accounts.

As for making this run on your Macs, you need to add the script into your JSS. How to do that will depend on how your JSS is set up. You may simply be able to add it directly in the Casper Suite UI.
If not, then it needs to be created in a plain text file with a .sh extension (recommend not to use TextEdit for this - get yourself a free copy of TextWrangler from the Mac App Store instead) and then that script will get added to your Casper Share. From there, it gets added into a policy and scoped to whichever Macs need it.
Then set your execution frequency and other settings like the trigger, maybe Recurring Check-in for example.

This is all covered in the full Casper Suite administrators guide. If you don't have that, recommend downloading it and start reading. You may want to actually start with the Quick start version since its more 'task' oriented.

sean
Valued Contributor

If you need to go below UID 500 make sure you don't delete the root account. You can use a similar command to get all admin users.

Eg. (as Mike said, change localadmin to the shortname of the admin account).

#!/bin/bash

excluded_user="localadmin"
local_admins=`dscl . read /Groups/admin GroupMembership | cut -d " " -f 2-`

function delete_admin_user
{
        while [ $# -gt 0 ]
        do
                case $1 in

                        root)
                                echo "$1 user"
                                ;;
                        $excluded_user)
                                echo "$1 intended admin"
                                ;;
                        *)
                                dseditgroup -o edit -d $1 admin
                                echo "$1 removed from admin group: "$?
                                ;;
                esac
                shift
        done
}

delete_admin_user $local_admins

exit 0

The dscl command should always return root first, so you could do a 'cut' of field 3 onwards and forget the check for root, but I'm not that brave to think this would always be the case!

The next step as you requested is making sure that no more occur. Of course, if they aren't admins then they won't be able to add admins, so at first glance doesn't look like it would be an issue.

However, whilst they have been admins they could have changed the password for your current admin account. This is where it could get complicated; checking that the admin account still has the correct password or reseting it. It's possible, but has debatable security.

sean
Valued Contributor

Yes, below 500 accounts are service accounts. They are hidden from the OS by default, but apart from root, none of these standard accounts should be part of the admin group. It is typical to add your own admin account as an ID below 500. If this is the case then Mike's script could be used without the grep.

However, since users have had the ability to add any accounts they like since they were admins, then this is why I opted for the version of checking all accounts for admin access and not just those above 500.

sean
Valued Contributor

In fact, they could have also enabled the root account too, creating a password for that. Not sure you can disable that without having to type a password or include username and password for an admin account that would be plain text in a script.

kitzy
Contributor III

Hi @MTurnerFMRCO,

I have a blog post on doing the opposite here.

You could probably modify the command to add the current user to the admin group to remove it instead like so.

dseditgroup -o edit -d $currentUser -t user admin

Make sure you test before putting into production.

cchichester
New Contributor III

Correct me if I am wrong, but the gist of the problem is that all domain users are local admins on macs that are joined to AD.

The first thing I would do is see how the machines were getting enrolled. If it was by a script with something like

dsconfigad -a <computername> -u <user> -p <password> -ou "OU=<ou>,DC=<dc>lan" -domain <domain> -groups "Domain Users"

Then besides removing any existing privileges, I'd do something like

dsconfigad -nogroups

to prevent future users that log in from being granted privileges. I have very limited experience with Macs joined to AD, but I believe that is how they may be getting the admin privileges in the first place.

MTFIDjamf
Contributor II

Thank you all for the replies. Unfortunately much of this is going over my head. No Mac training and no Casper training make Homer something something.....

Above 500 should be fine, I do not think anything below that was changed.

@kitzy do you think I could do a combo of mm2270's post above and yours? I have tried to run the command from mm2270 but it does not work. Comes back with errors in lines. Also, this cannot key off of a 'currentuser' type notation, it needs to be a generic variable and find all local admins on each machine and kill them (except for our main service account). Or, if I utilize the beginning part of the script from your blog, and add the line you have in your post here, should that work?

User's here should not be adding accounts, but anything is possible as they have been local admins for quite some time. When the devices are built, our desktop teams are adding the user as an admin utilizing Casper imaging, and sometimes allowing them to be admins after the fact if this step was missed. Until last week this was normal (not recommended but still normal) but now new polices are in place and this cannot be this way. All local admin rights have been removed from all Windows machines centrally already. Now, everyone is looking at the Macs....we basically have 800 Macs with 790 of them having domain users with local admin rights.

The reason that I would like it to continually run on the machines, once per startup or login, is that the desktop teams have the local service account credentials and can make people admins as they see fit down the road.

mm2270
Legendary Contributor III

@MTurnerFMRCO When you say you ran the script I posted and it "comes back with errors in lines" can you be more specific? What errors are you seeing?
Reason I ask is because, unlike some other scripts I sometimes post here, I actually did test this one on my own Mac. I have my AD account plus 2 local accounts I use for various testing purposes on my MacBook Pro. I made sure both of the local accounts were admins, then modified the script to exclude my AD account so the script wouldn't remove my own admin rights, then ran it with sudo*. It correctly noted that it was removing admin rights from both of the local accounts in the output. When I went back to check, both had become standard accounts, so its working for me with no errors.
Given this, can you give a little more detail on the problem you were seeing with it?

* Speaking of sudo, did you run the script by doing it with root privileges, as in sudo /path/to/script.sh? dseditgroup requires root, or you have to specify an admin account and password with flags in the command for it to work.

Also, @sean is 100% correct and I was wrong about the sub 501 accounts being in the admin group. I think I was thinking of another group, like lpadmin or something. Of those system level accounts, only root (UID 0) should actually be in that admin group, but its also possible your Casper Suite setup may have the Casper management account hidden on the Macs, and therefore with a UID below 501, so just be careful to make sure if you do try to have this go over all accounts from UID 1 and up that you don't mess up any of those accounts. I still think its safer to only target the higher UIDs myself.

mm2270
Legendary Contributor III
The reason that I would like it to continually run on the machines, once per startup or login, is that the desktop teams have the local service account credentials and can make people admins as they see fit down the road.

Just noticed this last sentence in your post. Sounds like your company needs to put some strict polices in place that govern what the desktop teams are allowed to do. Maybe that's already being discussed, and I hope so, otherwise it sounds like you'll be playing a game of whack-a-mole. The last thing you need is the "desktop teams" compromising your setup by willy-nilly making users local admins. They should be working in concert with your efforts, not against you, whether its intentional or not.

MTFIDjamf
Contributor II

@mm2270 Thanks for the continuing replies.
I did not run it with Sudo, was unaware. When I run it with that now, it asks for creds, seems to run, but none of my test accounts have lost their Admin rights. Reboot made no difference. Seems better than before, but is not doing what I need it to do, yet. Should the output that you mention be in the Terminal window itself?

If I understand correctly, to add any sort of Sudo command to the script itself, it will have to call Admin level creds to run at all. How exactly should that look?

The policies are coming down and the desktop teams should not be doing it, in all honesty, never should have been doing it in the first place. They should all be coming in line.

mm2270
Legendary Contributor III

@MTurnerFMRCO OK, so, maybe we need to take a few steps back to see where the problem is stemming from.

First, how are you creating the script? I assume you copied and pasted what I wrote above, but what application did you paste into? What extension did you give the script?
Shell scripts are very picky, and any odd formatting, like rich text type stuff will simply make it fail to run. I suspect that isn't the case or you would have seen some errors when running it in Terminal, but I want to cover all bases. Also, scripts that aren't created in an application that is designed for script writing must be made executable to run, typically. I suggest using TextWrangler (free from the Mac App Store), but there are other tools as well for script writing. I don't suggest using TextEdit unless you make several changes to the app's preferences and are careful when using it. TextEdit loves to do rich formatting of text, which as I mentioned, shell scripts don't like.

Second, can you just run the following command in Terminal to see what its returning?

dscl . list /Users UniqueID | awk '$2>500 {print $1}'

That should be returning a list of your local account names. Not whether they are admins or not (we'll get to that later), but just a list of names. If its not returning anything for some reason, than that would be the problem.

Post back with whatever answers you can on the above questions.

MTFIDjamf
Contributor II

@mm2270 Few steps back works for me.

As the work on Macs has only recently come into play for me I have been doing everything in Windows notepad, saving as a .sh file and then copying this file to a test Mac that I have received. I am guessing that this is probably not helping my cause at all. I have downloaded TextWrangler on the Mac from the web (AppStore is turned off via Casper, no access for anyone) and will try it that way. Basically, I am not super familiar with OSX so just the basic navigation around the OS takes time at the moment...

Running the command that you specified does list the account names, both domain and local.

MTFIDjamf
Contributor II

@mm2270 Copying your input and using TextWrangler on the Mac it ran when utilizing Sudo in terminal without issue after I enter creds. So, since that definitely works (thank you!) how do I alter the script to run with the Sudo privileges each time so that I can now figure out how to get this out via Casper? Or, filp this over, can Casper run this script at machine start or user logon with elevated privileges without altering the script?

mm2270
Legendary Contributor III

Ok, sure, the regular web version of TextWrangler is basically the same thing as from the App Store, so that works.
Ok, so if the command is returning the user names, generally speaking the script should work, but let's do one intermediate step here before we go back to it.

Copy/paste the following script into TextWrangler, make sure to save it with a .sh extension to a Mac, then run it in Terminal by doing something like this: (note no need for sudo since we aren't making any changes)

/path/to/script/scriptname.sh

You can also run the script directly in TextWrangler by going up to the #! menu and choosing "Run" while the script is the open active window. That's generally what I do when doing quick tests.

Here's the script:

#!/bin/bash

localAccts=$(dscl . list /Users UniqueID | awk '$2>500{print $1}')

while read account; do
    echo "Found account: $account. Checking admin status"
    dseditgroup -o checkmember -m $account -t user admin
done < <(echo "$localAccts")

Quick explanation: This version simply loops over the accounts it finds, echoes back the account name, and checks to see if its in the admin group. The output in Terminal should show the account name being mentioned as well as if its part of the admin group.
Run that and let's see what its returning.

mm2270
Legendary Contributor III

OK, good, looks like its working, so no need to run thru the intermediate step above - just ignore that than :)

As for running your script in Casper, Casper Suite takes care of that. It runs scripts as root or with root privileges by default, so just create or upload the script into your JSS, then add it to a policy and scope it to one or two test Macs. Make sure you set some kind of trigger and frequency for the policy and let it run.

MTFIDjamf
Contributor II

@mm2270 Again, thank you for the help.
I have uploaded the script to the Casper share and the script shows in my console. I have created a policy and applied the script to it. Now, if I make this available via Self Service will it remain available there even after it has been selected to run? I know in the past, some items that were in Self Service we would run and it would disappear after running. No idea if this is a setting I can change or not, I would prefer it to always remain visible, is this possible? Also, if we make it available in Self Service and set another time for it to run, will both still be possible?
Also, is there a way to ensure it runs as each machine is powered up? Or say, once per day/at JSS check-in? I see these settings under general in the policy settings but not sure exactly what they really do. If I select startup, will this really run at each device startup, every single startup? Am I better off selecting the Recurring check-in option? Basically it HAS to run once per machine, after that is up for discussion and training of our desktop people. Meaning, what is the best bet to get it to run at least one time on each device? If someone runs it via self service, will a recurring check-in setting still run anyway?

What I expect to be mandated from above is that it runs as each Mac comes online and is available in Self Service to start. It can run multiple times per machine as it does not hurt anything. Then, once the numbers reduce, there will be a change to the frequency.

Is there a location in Casper or on the local device to know each time the script has run?

Apologies for all of the questions, I see some of this in the admin guide but it definitely seems that the prior person in charge of this has taken liberties with some settings and I am unfamiliar with the rest.

mm2270
Legendary Contributor III

Hi @MTurnerFMRCO No worries on all the questions. Let me see what I can answer for you.

First, I'm not certain that something like this actually makes sense to put into Self Service. Its up to you of course, but generally speaking, Self Service items are for things that users may voluntarily want to run, like installer a new piece of software, adding printers, etc. Something like "remove your admin rights from this Mac" doesn't sound like an appealing Self Service policy to me. I suppose you could name it and word it in a more appealing way, but that's just my 2¢ on that.
As for Self Service polices running once and disappearing, yes that is a setting you can change. Policies default to a Execution Frequency of "once per computer" but you can choose Ongoing or some other setting from the Frequency menu in the policy when you edit it. Ongoing will make something appear in SS indefinitely. The other settings will make it show up at whatever frequency it states.

For something like this policy, it may make more sense to have it run on either Recurring Check in or Login, but not so sure how well it would work on Startup. I've never tried running something like this on Startup. I guess try it out on a few systems and see.
Also, instead of having this policy always running at every login or every check-in, you may want to consider using an Extension Attribute that would gather admin status of local accounts. Then create a Smart Group of any Macs that report in with having a local account that has admin rights.
If you look over this Feature Request thread, several of us posted example Extension Attribute scripts that would gather this information for you.

EDIT: Also, re:

Is there a location in Casper or on the local device to know each time the script has run?

You will want to look in the policy log. It will show times and results of each run per Mac.

bpavlov
Honored Contributor

@MTurnerFMRCO You're getting good help here from Mike. Nothing to add myself regarding your problem. Just wanted to add that you can also learn a bit more about Mac management by watching some videos online from PennState MacAdmins: http://macadmins.psu.edu/conference/resources/ which cover a lot of different topics.

I would also take the time and read the Casper documentation through at least once.

And there are a lot of other posters here and elsewhere that provide some very good information on their blogs too:
https://derflounder.wordpress.com is by @rtrouton

And check out this blog too:
http://rustyisageek.blogspot.com/p/home.html
Rusty Myers doesn't post as frequently but every now and then does. He has also compiled quite a decent list of other Mac-centric blogs on the right side which you should take a look through.

Anyways, I now return you to your thread.

MTFIDjamf
Contributor II

@mm2270 I agree with the self service comments, I just wanted to know the behavior as some people love self service and want everything we setup to show up there.

So, if I set it up as recurring, with a reboot (hypothetical), will that mean that it just runs every check-in (15 mins) and reboots? Will it simply not do anything if there are no admin accounts or will it just continue to run and reboot every 15 minutes regardless of admin account presence?

I am leaning towards running at login but I see that it specifies a login hook that checks for policies must be configured. I am unsure if that exists here.
If that does not work I will start as ongoing at JSS check-in with no reboot to at least get this in motion.

Luckily, the prior Casper admin already has a smart group created that calls out the devices with local admin rights so I can apply it there and not have this running on all devices.

MTFIDjamf
Contributor II

@bpavlov Agreed. The help has been outstanding and extremely appreciated. I will be turning to these forums quite a bit, that's for sure.

The Casper admin docs are good, have been using them, problem is comparing it to what is already setup here and what are the differences between the doc and this current real life setup.

Thank you for the links, I will definitely check them out. hopefully getting to a Mac training class before the end of the month.

mm2270
Legendary Contributor III

@MTurnerFMRCO You definitely do not need to run this with any reboot in the policy afterwards. Making the changes from admin to standard with dseditgroup is immediate and doesn't require a reboot to take affect. Even if the account it changes is logged in when the change happens, they are no longer admins right after the script runs and can't run admin level commands, installers, unlock protected Preference Panes etc.

Luckily, the prior Casper admin already has a smart group created that calls out the devices with local admin rights so I can apply it there and not have this running on all devices.

If that's the case, then yes, I highly recommend scoping the policy only to that group and not just all Macs. Having a policy run repeatedly on your systems if its not needed, isn't usually best practice. It negates some of the power of the Casper Suite if you don't build and use dynamic Smart Groups.