Managing WiFi on macOS

delbrown
New Contributor II

A few years back, I was in a school within a community with a large cluster of open WiFi networks. The problem we had was students dropping off the filtered School WiFi and connecting to one of the neighborhood networks.

To address this issue, I created a script to ensure that students would get kicked off any other network if the school network was available. The attached Flow chart illustrates the logic of the script.b1f8da96f86541f88e2fb35ac01b4781

The script is below without any modification from it's original use. Note that in the FlowChart it checks to see if a device was allowed on the internet...and reads that info from a public fileserver..but that's another discussion.

The individual functions line up with the flow chart boxes. The script was triggered using a launchd item (which follows) which triggered on a network change.

#! /bin/bash
#########################################################################
#                                                                       #
#                           manage wifi networks                        #
#                                                                       #
#                                                                       #
#                                                                       #
#                             Del Brown                                 #
#                               2011                                    #
#                          delbrown@me.com                              #
#                                                                       #
#                                                                       #
#                                                                       #
#                                                                       #
#                                                                       #
#########################################################################

# Define Variables Here
# Set the approved Wireless Network Name(s) in the WifiWhitelist variable
WifiWhitelist="Lyman_Moore_MS Lincoln_MS King_MS ProjectOffice"

# Set the Location of the AccessList file which determines if student is allowed internet access
#AccessCheck="http://www.delbrown.net/mlti/AccessList"
AccessCheck="http://staff.portlandschools.org/brownde/mlti/AccessList"

########################### DO NOT MODIFY BELOW THIS LINE   ###############################
#check for current WiFi name
MyWifi=`networksetup -getairportnetwork Airport | awk '{print $4}'`

WifiAvailable=`/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -s | awk '{print $1}'`

# This can also be based on the serial number if you wish
#Asset=`ioreg -l | grep IOPlatformSerialNumber | sed 's/"//g' | awk '{print $4}'`

Asset=`nvram asset-tag | awk '{print $2}'` #this is where I have stored the Asset Tag Info

# Check for Access
Access=`curl -sf $AccessCheck | grep -i $Asset | awk '{print $2}'`

# Get Working Directory
WorkDir=`dirname "$0"`

# End of Variable Definitions

########################### Functions ###########################

disconnect ()
{
# Notify user that the network is not approved and disconnect from the wireless network.
./BigHonkingText $MyWifi is not an approved Network
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -z
exit 1
}

atSchool ()
{
# test to see if the Asset is at school by scanning for school networks and see if one is the school
for ScannedNetworks in $WifiAvailable
    do
        for SchoolNetwork in $WifiWhitelist
            do
                if [ "$SchoolNetwork" == "$ScannedNetworks" ]
                    then
                        AssetAtSchool="Yes"
                        return
                fi
            done
    done
AssetAtSchool="No"
return
}

onSchoolNetwork ()
{
# test to see if the school network has been joined.  
# Call the disconnect function if a network outside of school is joined
for AllowedID in $WifiWhitelist
    do
        if [ "$AllowedID" == "$MyWifi" ]
            then # Asset is on the school network
            exit 1  
        fi
    done
disconnect
exit 2
}

checkAccess ()
{
# look up the list to see if home access has been turned off
Access=`curl -sf $AccessCheck | grep -i $Asset | awk '{print $2}'`

}

########################### End Functions ############################

# Change to Working Directory
cd "$WorkDir"

# Is the Asset at school?
atSchool

if [ $AssetAtSchool == "Yes" ]
    then
        # Is the Asset on the School Network
        onSchoolNetwork
fi

# If we get here then the computer is at home...or cannot connect to the school network

# Is the student allowed on the internet at home?

checkAccess

if [ "$Access" == "No" ]
    then
        disconnect
fi

# Everything is good and we can exit
echo "Everything is A-OK"
exit 1

Net manage plist follows:

<?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>net.delbrown.NetManage</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/delbrown.net/bin/NetManage</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
    </array>
</dict>
</plist>
12 REPLIES 12

stevewood
Honored Contributor II
Honored Contributor II

@delbrown might want to edit your post or repost so the script shows up properly. Looks like you pasted the script outside of the "grave" ticks that mark it as a script.

jjones
Contributor II

I can see this coming in hand with some students using their phones as hotspots for our school.

I do have one issue that seems to be coming up with this section:

Asset=`nvram asset-tag | awk '{print $2}'` #this is where I have stored the Asset Tag Info

It is giving me this error when I attempt to run the script:

bash-3.2$ Asset=nvram asset-tag | awk '{print $2}' #this is where I have stored the Asset Tag Info nvram: Error getting variable - 'asset-tag': (iokit/common) data was not found

delbrown
New Contributor II

Thanks @stevewood. I figured it out after I viewed it again.

delbrown
New Contributor II

@jjones That part references a variable in nvram where I used to store the devices asset tag to make it persistent after imaging. This was part of the "Other Discussion"

delbrown
New Contributor II

After looking at the unfinished nature of my original script. I found a little time and updated my original script to simplify the option and work on the updated macOS available. This script is ready to go. Just create a valid Launchd action and run the following:

#!/bin/bash

# Monitor and Manage WiFi Networks                      
# Del Brown                                  
# 11/21/16                                
# delonline@icloud.com                     
#                                                            
# Begin Variable Definitions
#Replace WIFINAME with your school WiFi.  Leaving it empty will disconnect from any network.

WifiWhitelist="WIFINAME"

# End of Variable Definitions

# Begin Function Declarations
connect ()
{

for SchoolNetwork in $WifiWhitelist
    do
        #loop through whitelist and connect to whitelisted network found
        echo " Available Network ""$SchoolNetwork"
        networksetup -setairportnetwork en0 "$SchoolNetwork" &>/dev/null
    done
}

disconnect ()
{
echo "Time to disconnect"
# send disconnect command to en0
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -z
#exit 1
}

onSchoolNetwork ()
{
# test to see if the school network has been joined.  You can either use the networksetup command or the airport utility for this

MyWifi=`networksetup -getairportnetwork en0 | awk '{print $4}'`
#MyWifi=`/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport --getinfo | grep " SSID" | awk '{print $2}'`

# Call the disconnect function if a network outside of school is joined
for AllowedID in $WifiWhitelist
    do
        if [ "$AllowedID" == "$MyWifi" ]
            then # Asset is on the school network
            echo "I am connected to the School Network ""$AllowedID"
            exit 1  
        fi
    done

echo "Device is not connected to School Network so disconnect and reconnect to the school"
disconnect
connect
exit
}


atSchool ()
{
WifiAvailable=`/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -s | awk '{print $1}'`
# test to see if the Asset is at school by scanning for school networks and see if one is the school
for ScannedNetworks in $WifiAvailable
    do
        for SchoolNetwork in $WifiWhitelist
            do
                if [ "$SchoolNetwork" == "$ScannedNetworks" ]
                    then
                        AssetAtSchool="Yes"
                        echo "The Device is at school"
                        return
                fi
            done
    done
AssetAtSchool="No"
echo "asset is not at school so we don't care and we'll exit"
exit
}

# End Function Declarations

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

#program starts here
atSchool
onSchoolNetwork

sdagley
Esteemed Contributor II

@delbrown Thanks! You just saved me some work.

lizmowens
New Contributor III

OK, @delbrown , this looks exactly like what I need, but my complete lack of scripting knowledge is making this a challenge. I'd love some help if you have time! I get that I have to change the Wifi whitelist name to our student network name, but after that, I have no idea what pieces to copy, what pieces I need to edit...I've successfully pushed out some really basic scripts via Casper Remote, like the one that requires them to authenticate with admin credentials to turn off the wifi, but that was a one-line script I just had to copy/paste. :)

sdagley
Esteemed Contributor II

@lizmowens If you look at the original post by @delbrown at the top of this thread, you'll see that he shows both the .sh script that manages the WiFi settings (although the one you want to use is further down in the thread) and the .plist LaunchAgent that will trigger the script when the AirPort preferences file changes. You need to modify those for your environment and then create a package in Composer you can deploy to your student machines.

Search on Jamf Nation for LaunchAgent to find info on that topic.

You may also find this site useful: lauchd info

This is a very useful app for dealing with LaunchAgents/LaunchDaemons: LaunchControl

j_tanudjaja
New Contributor III

Thanks! amazing script, any ideas how can I modify the script if my SSID have a whitespace? e.g "STUDENT WIFI"
I tried to use or just but it doesn't seem to work

BMull
New Contributor

@j.tanudjaja I'm working on the same issue. I've made the following changes:
WifiDevice=$(networksetup -listnetworkserviceorder | grep 'Wi-Fi' | grep 'Device' | cut -d ":" -f3 | rev | cut -c 2- | rev | awk '{$2=$2}$1')
MyWifi=$(networksetup -getairportnetwork $WifiDevice | cut -d ":" -f2 | awk '{$2=$2}$1')
This gets the currently connected SSID. I need WifiDevice as some computers use en0 while others use en1.

WifiAvailable=$("/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport" -s | cut -d ":" -f1 | cut -d ")" -f2 | awk '{$2=$2}$1' | rev | cut -c 4- | rev | sort | uniq)
This gets a list of SSIDs and removes leading/trailing spaces and duplicates.

As we have multiple SSIDs, with spaces, that devices can connect to I'm looking into using an array for the WifiWhitelist. Should just need a change in the atSchool () function to work.

BMull
New Contributor

I've made modifications to Del Brown's script to support spaces in the SSIDs. Next is to force connection to a saved network profile while at school but that can wait for now.

#! /bin/bash
#########################################################################
#                                                                       #
#                           manage wifi networks                        #
#                                                                       #
#                                                                       #
#                                                                       #
#                             Del Brown                                 #
#                               2011                                    #
#                          delbrown@me.com                              #
#                                                                       #
#                                                                       #
#                                                                       #
#                                                                       #
#                                                                       #
#########################################################################
# Modified to support SSIDs with spaces BMull   10-Dec-2019

# Define Variables Here
# Set the approved Wireless Network Name(s) in the WifiWhitelist variable
# As we have spaces in SSIDs will use an array
declare -a WifiWhitelist
WifiWhitelist=("SSID 1" "SSID 2" "SSID 3" "SSID4")

########################### DO NOT MODIFY BELOW THIS LINE   ###############################
#check for current WiFi name
WifiDevice=$(networksetup -listnetworkserviceorder | grep 'Wi-Fi' | grep 'Device' | cut -d ":" -f3 | rev | cut -c 2- | rev | awk '{ gsub(/^[ 	]+|[ 	]+$/, ""); print }')
MyWifi=$(networksetup -getairportnetwork $WifiDevice | cut -d ":" -f2 | awk '{ gsub(/^[ 	]+|[ 	]+$/, ""); print }')

# Needed below to work with SSIDs with spaces in arrays; MUST be after WifiDevice/MyWifi or will get an error
SAVEIFS=$IFS
IFS=$(echo -en "
")

apPath="/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources"
declare -a WifiAvailable
WifiAvailable=( $("$apPath/airport" -s | cut -d ":" -f1 | cut -d ")" -f2 | awk '{ gsub(/^[ 	]+|[ 	]+$/, ""); print }' | rev | cut -c 4- | rev | sort | uniq | tr "
" "
") )

# Get Working Directory
WorkDir=$(dirname "$0")

# End of Variable Definitions

########################### Functions ###########################

disconnect ()
{
# Notify user that the network is not approved and disconnect from the wireless network.
./BigHonkingText "$MyWifi is not an approved Network"
$apPath/airport -z
}

atSchool ()
{
# test to see if the Asset is at school by scanning for school networks and see if one is the school
for ScannedNetworks in "${WifiAvailable[@]}"
    do
        for SchoolNetwork in "${WifiWhitelist[@]}"
            do
                if [ "$ScannedNetworks" == "$SchoolNetwork" ]
                    then
                        AssetAtSchool="Yes"
                        return
                fi
            done
    done
AssetAtSchool="No"
return
}

onSchoolNetwork ()
{
# test to see if the school network has been joined.  
# Call the disconnect function if a network outside of school is joined
for AllowedID in "${WifiWhitelist[@]}"
    do
        if [ "$AllowedID" == "$MyWifi" ]
            then # Asset is on the school network
            IFS=$SAVEIFS
            exit 0 
        fi
    done
disconnect
IFS=$SAVEIFS
exit 2
}

########################### End Functions ############################

# Change to Working Directory
cd "$WorkDir" || return

# Is the Asset at school?
atSchool

if [ "$AssetAtSchool" == "Yes" ]
    then
        echo "Asset is at School..."
        # Is the Asset on the School Network
        onSchoolNetwork
fi

# If we get here then the computer is at home...or cannot connect to the school network

# Everything is good and we can exit
echo "Everything is A-OK"

IFS=$SAVEIFS

exit 1

mattedmonds
New Contributor II

Could someone please help with this? like @lizmowens I'm a little lost ... I copied the script with our own WiFiWhitelist variables and converted the plist using plutil and put it in /Library/LaunchAgents yet it doesn't seem to work. My apologies, I'm such a novice with this stuff