Contents of script asking for admin password.

ncottle
New Contributor III

We have a script that we are trying to allow a user to run without having the need to input admin credentials. Basically the script checks for a valid IP on Ethernet and if it finds it, it shuts off WiFi and exits. If it doesn't see a valid IP on Ethernet it checks WiFi and looks for a valid IP. If it finds it, it exits. If it doesn't it shuts the airport off, removes all the networks, turns airport back on, installs a configuration profile and reconnects up to our network.

The script works perfectly but as soon as we deploy it to a test box as either a .command or as a .app it prompts for admin credentials to turn the WiFi off, then again to turn it back on and again to install the profile. After researching it we are at a loss as to how to allow the contents of the script to run without interruption.

Our end goal is to have an app or an alias to a .command file that resets the wireless setting on site by the current user who is not an admin on the machine. Any help, ideas or guidance would be very much appreciated.

Here is a copy of the script. We using modified version of tkimpton's awesome script at https://jamfnation.jamfsoftware.com/discussion.html?id=5327

#!/bin/bash
#
####################################################################################
#
# Set the variables
#
####################################################################################

# Get the ethernet hardware port (ehwport) and ip address (eipadd)
ehwport=networksetup -listallhardwareports | awk '/.Ethernet/,/Ethernet Address/' | awk 'NR==2' | cut -d " " -f 2

# Get the ethernet ip address (eipadd)
eipadd=ipconfig getifaddr $ehwport

# get the first six of the addy
eipsubnet=$( echo "$eipadd" | cut -d. -f1,2 )

echo "The ethernet hardware port is $ehwport at address $eipadd"

# Get the wireless network service (wservice)
wservice=/usr/sbin/networksetup -listallnetworkservices | grep -Ei '(Wi-Fi|AirPort)'

echo "The wireless network service is $wservice"

# Get the wireless hardware port (whwport) and ip address (wipadd)
whwport=networksetup -listallhardwareports | awk "/$wservice/,/Ethernet Address/" | awk 'NR==2' | cut -d " " -f 2

# Get the wireless ip address (wipadd)
wipadd=ipconfig getifaddr $whwport

# get the first six of the addy
wipsubnet=$( echo "$wipadd" | cut -d. -f1,2 )

echo "The wireless hardware port is $whwport at address $wipadd"

# Get the wireless network (wirelessnw)
wirelessnw=networksetup -getairportnetwork $whwport | cut -d " " -f 4

echo "Wireless network is $wirelessnw"

# Carry out an OS version check
OS=/usr/bin/defaults read /System/Library/CoreServices/SystemVersion ProductVersion | awk '{print substr($1,1,4)}'
echo "The OS is version $OS"

# Check to see if the JSS is available and if yes, then submits the current IP
checkjss=/usr/sbin/jamf checkJSSConnection -retry 0 | grep "The JSS is available"
echo JSS is Available

####################################################################################

# Test the ethernet connection, reset wi-fi and reconnect to SPS_Secure if necessary

if [[ "$eipsubnet" == "123.456" ]]; then networksetup -setairportpower $whwport off echo "Ethernet is active at IP : "$eipadd"" exit 0

else
# Turn off wifi
networksetup -setairportpower $whwport off

# Remove All Wireless networks
/usr/sbin/networksetup -removeallpreferredwirelessnetworks $whwport

# Do not ask to join new networks
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport prefs joinmode=automatic joinmodefallback=donothing

echo "Set the wireless service to not ask to join other wireless networks"

# Turn on wifi
networksetup -setairportpower $whwport on

fi

####################################################################################
#
# Determine the OS version and either import the config profile (10.7 and up) or import the network settings (10.6)
#
####################################################################################

# If 10.6 Import the network settings This step requires the config profile to be staged first

if [ $OS = 10.6 ]; then networksetup -import8021xProfiles AirPort /private/var/tmp/thawte.networkconnect exit 0

else

# If 10.7 or newer Import the wireless configuration profile. This step requires the config profile to be staged first

profiles -I -F /private/var/tmp/8021xprofile.mobileconfig exit 0

fi

####################################################################################
# Check to see it the jss is ping-able and if so submit the ip change to the jss.
# This is useful when using Casper Remote and users are switching between wired and wireless
#####################################################################################

if [ "$checkjss" == "The JSS is available." ]; then
echo JSS is Available
fi
exit 0

1 ACCEPTED SOLUTION

gregneagle
Valued Contributor
Only because the launchd "WatchPath" construct requires the watched file or folder to be present or the launchd disables itself.

You can avoid this by not using a WatchPath. Instead use "KeepAlive" with "PathState":

<key>KeepAlive</key>
  <dict>
     <key>PathState</key>
     <dict>
       <key>/private/tmp/.my_special_trigger_file</key>
       <true/>
     </dict>
  </dict>

Creating ('touching') this file causes the LaunchDaemon to start the job (script). Be sure your script removes this file, or it will be restarted again because the trigger file still exists.

View solution in original post

8 REPLIES 8

gregneagle
Valued Contributor

Make it a postflight script for a payload-free package. Add the package to Self-Service. Users can then "install" the package, which runs the script as root, and avoids the need for admin credentials.

donmontalvo
Esteemed Contributor III

...maybe even take advantage of Casper Suite 9.3's Network State Change and enable Make Available Offline policy criteria.

New Policy > Options > General > Trigger > [x] Network State Change

and

New Policy > Options > General >[x] Make Available Offline

--
https://donmontalvo.com

mm2270
Legendary Contributor III

My initial thought was also "why not just throw this into Self Service? That's what it was designed for." But if the script was designed to be run in the event that the Mac doesn't have a valid IP that possibly removes Self Service from the equation since SS requires a network connection to the JSS to be used (If only JAMF would figure out some way to show "offline" policies in Self Service when the app can't connect to the JSS, it would be an option, but alas, we can't do that as of yet)

My suggestions then would be to do possibly @donmontalvo mentions and make it available offline and use the NetworkStage Change trigger. But this could lead to the policy running quite frequently, and may not be exactly what you want.
If this really needs to be user initiated, although I don't completely understand why that would be, another option would be to create a small script/app that would make a change to a local writable file on the Mac, such as adding a timestamp to it, and use that file as the WatchPath for a LaunchDaemon that would trigger the LaunchDaemon to run the above script. Since that would run the script as "root" it won't prompt for any passwords.

The only thing is, you'd need to make sure the package you build would deploy the file and the LaunchDaemon and activate the Daemon all in one shot, with say a postinstall script. I would also make sure the file in question is buried well enough that casual browsing won't turn it up. Only because the launchd "WatchPath" construct requires the watched file or folder to be present or the launchd disables itself.

gregneagle
Valued Contributor
Only because the launchd "WatchPath" construct requires the watched file or folder to be present or the launchd disables itself.

You can avoid this by not using a WatchPath. Instead use "KeepAlive" with "PathState":

<key>KeepAlive</key>
  <dict>
     <key>PathState</key>
     <dict>
       <key>/private/tmp/.my_special_trigger_file</key>
       <true/>
     </dict>
  </dict>

Creating ('touching') this file causes the LaunchDaemon to start the job (script). Be sure your script removes this file, or it will be restarted again because the trigger file still exists.

ncottle
New Contributor III

Thanks for all the input so far. Looks like we are going the launchd route. We need it to be user initiated and offline since the users that will be running this will never have admin rights and they are fantastic at breaking internet connections. Thank you all again.

ncottle
New Contributor III

Thank you @gregneagle, we went the WatchPath route and after a bit of fumbling with the permissions it works like a charm. Our users now have the ability to fix a lot of their connection issues on their own (one of our most common HelpDesk Tickets). Again, a big thanks to everyone else for their input.

tlarkin
Honored Contributor

Hi @nick.cottle

I know I am a bit late to the replies here, but here are some additional thoughts that may be useful. Casper has a feature where you can run things in 'offline mode.' I think it could be an option to explore. In version 9 we have a policy trigger called Network State Change, which basically watches the path of a certain file, and if that file is changed (network change, hostname change) it can trigger the script. Having it set to ongoing and checking the box to make it available offline will cache the script locally, run it as root with the Casper framework, and run your script as you intend. This way you won't need to manage and deploy a launchd item, load it, and support it down the road.

This way the script is cached and ran locally, it is ran as root, and since you have already built it, there is no need to build anything further on top of what you have already coded to make it work. There is absolutely no issue going the launchd route at all, as this is just another way to do it with the built in tools Casper already offers.

I have used this method for login scripts that need to be ran regardless if the device is on network or not. Just note using a watchpath in a launchd will always trigger the script the second that file is modified in any way. Using the network state change trigger for a policy would operate in the same manner. So, I would be careful on how you implement the solution so you don't cause any headache for yourself. An example of a bad practice would be to set up a networkstate change policy that ran a recon, or did an API call. That means every time the user puts the laptop to sleep or changes networks you would be submitting a recon or invoking an API call. If devices do this in mass, it could cause problems.

Since your script is ran locally and does not do either of the two things I mentioned, I don't see much of a problem in that regard, but I figured I would toss out an example. Since Casper already does this, there would be no reason to deploy it as a .command file or an app. Just remember to test it properly before deploying.

So really I just basically explained what @donmontalvo suggested.

I hope this helps some of you.

-Tom

ncottle
New Contributor III

Thanks @tlarkin for the input, I have been out of the office for a couple weeks (new baby) so I couldn't reply. In the end we did go with a .command and WatchPath because we were able to have the end user run it on their own only when an issue arose. After some testing and other input it was the best fit for our needs. Thanks again, I hope that all made sense. Still a little low on sleep.