Network State Change

jared_f
Valued Contributor

Hi All,

Is it possible to call a script with a network state change locally. I have been looking for a .plist to do this, but I cannot find one.

Jared

15 REPLIES 15

mm2270
Legendary Contributor III

Can you elaborate on what you're trying to do? Are you asking if there's a custom trigger that would call a Jamf Pro policy that uses the Network State Change trigger, or are you asking for a way to trigger an artificial network state change so a policy will run?

jared_f
Valued Contributor

@mm2270 This is all serrate from Jamf Pro. I would like the machine to locally know when there is a network state change (i.e. user changed networks) and then call upon a script to run (which I already have written).

Thanks for your help,
Jared

mm2270
Legendary Contributor III

@jared_f Ah, I see then. Ok, so in that case, what you're looking for is a launchd job, meaning either a LaunchDaemon or a LaunchAgent. Which one really will depend on a) when you want it to run and how and b) if what you plan on doing requires elevated or root rights to perform.

If the command or package installer or what-have-you depends on root privileges then your only real option is a LaunchDaemon as those run as root. If you plan on affecting something in the user space and want it to execute as the logged in user, a LaunchAgent will do the trick.
In either case, what you want to include in the launchd job is a key called WatchPaths. You would include the following path to watch changes for:
/Library/Preferences/SystemConfiguration

There are some files (plists) inside that directory that get touched or changed on any network state change. Unfortunately, sometimes they get changed on other non network change events, so your script may end up being triggered at times when you don't want it to. Depending on what your script is doing, you may want to consider putting some extra code in it to determine if the reason it was called to run is valid or not before doing anything else.
Also be aware that in past OSes we ran into a problem where for some reason the script was being called very frequently by the Launchd job. There was some bug in the OS that kept touching one of the plist files in that directory almost constantly. I don't think it's an issue any more, but I would recommend adding in a 10 second sleep at the end of the script to prevent it from running any more often, just in case. Launchd jobs get throttled by the OS to only run once per 10 seconds, max. If it tries to run it any more than that it starts dumping all kinds of errors into the system.log about it.

Here's an example of a LaunchDaemon plist using that key and how you might set it up

<?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.org.jobname</string>
    <key>ProgramArguments</key>
    <array>
        <string>/path/to/script/myscript.sh</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Library/Preferences/SystemConfiguration</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

I would suggest using a program, like LaunchControl for example, to create the Launchd job. It takes the guesswork out of the syntax and permissions to make sure the job will run correctly.

Hope that helps.

donmontalvo
Esteemed Contributor III

I always wondered if Jamf used crankd.

--
https://donmontalvo.com

Swift
New Contributor II

NetworkStatusWarden can be used to run your own custom scripts when the primary network interface changes state.

el2493
Contributor III

Just wanted to see if anyone had come up with a good solution for this that doesn't cause the computer to check-in every minute or two, as @mm2270 mentioned? I know I could sleep it at the end of the script, but that's not an ideal workaround. I have a script that detects if a computer is connected to our unsecure network and if it is it disconnects and gives a message to connect to our secure network (it's more complicated than that, but that's the gist of it). I had planned on making a Policy with the script, setting the Trigger to Network State Change, and setting it to Make Policy Available Offline. All of that works, but I didn't expect to see so many entries in Logs (each computer that it runs on seems to run again about a minute later).

I found another user recommending using "/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist" in the launchd recommended above rather than just "/Library/Preferences/SystemConfiguration/", which I assume wouldn't register changes to Ethernet connection, but for my purposes that wouldn't be a problem. I was just wondering if anyone had any experience doing that or any other recommendation.

slundy
New Contributor III

Looking for something very similar to what you all are working on. We have a handful of macs that disconnect from the network at random times, but only during after hours. I downloaded NetworkWarden but am not sure how to use it to fire off an email, or write to a log file on reconnect and put it on a share. We're trying to track down why this happens (all machines are wired, and it happens even with caffeinate running in Terminal.

So I wanted to script something to alert me (through email) using the network state change trigger. They're mostly iMac's with a couple of macbooks.

Look
Valued Contributor III

I just happened to be working on something along these lines yesterday.
The most effective way I could work out for detecting a new network connection was a LaunchDaemon watching

/private/var/db/dhcpclient/leases

That calls a script.
If your looking for connections to a certain subnet you can do an awk of the latest changed lease file for the subnet you are after, something like this.

#!/bin/bash
My_Subnet="10.10"
Changed_File=$(ls -t -1 /private/var/db/dhcpclient/leases | head -1)
if [[ "$Changed_File" ]] && [[ "$(cat "/private/var/db/dhcpclient/leases/$Changed_File" | awk /$My_Subnet/)" ]];then
SOME_ACTION_HERE
sleep 10
fi

The files only change on connection to a new network, so it won't detect disconnects, but it will pick up reconnects. I have no idea what it does for none DHCP networks though.
It does have way less false positives than the jamf one though! For my purposes that only require detecting connecting to a certain network it works really well! (I am calling a policy by custom trigger as my action).

donmontalvo
Esteemed Contributor III

@Look this is awesome. One observation, the /private/var/db/dhcpclient/leases file doesn't seem to get modified if/when user connects/disconnects on Big-IP Edge client (not sure about other VPN clients).

Checked the file while toggling VPN connection and this is what I get:

bash-3.2# ls -l /private/var/db/dhcpclient/leases
total 8
-rw-r--r--  1 root  wheel  1312 Jan 16 07:04 en0-1,xx:xx:xx:xx:xx:xx
bash-3.2# ls -l /private/var/db/dhcpclient/leases
total 8
-rw-r--r--  1 root  wheel  1312 Jan 16 07:04 en0-1,xx:xx:xx:xx:xx:xx
bash-3.2# ls -l /private/var/db/dhcpclient/leases
total 8
-rw-r--r--  1 root  wheel  1312 Jan 16 07:04 en0-1,xx:xx:xx:xx:xx:xx
bash-3.2# ls -l /private/var/db/dhcpclient/leases
total 8
-rw-r--r--  1 root  wheel  1312 Jan 16 07:04 en0-1,xx:xx:xx:xx:xx:xx
bash-3.2# ls -l /private/var/db/dhcpclient/leases
total 8
-rw-r--r--  1 root  wheel  1312 Jan 16 07:04 en0-1,xx:xx:xx:xx:xx:xx
bash-3.2#

Curious which file in /Library/Preferences/SystemConfiguration/ folder would be monitored? Or some other file?

--
https://donmontalvo.com

hansjoerg_watzl
Contributor II

As our new Jamf Pro server is behind a reverse proxy, Jamf is not able to report the IP address of a Cisco AnyConnect VPN connection anymore.
I wrote a small script, which gets the Cisco VPN IP address and writes it directly to an extension attribute (with API, so it's faster and more light weighted than recon).
The command is:

/opt/cisco/anyconnect/bin/vpn -s stats | grep 'Client Address (IPv4):' | awk '{print $NF}'

The script will be triggered by a policy on every network change. This works so far, but it generates a lot of policy logs, as it will be triggered by ANY network change (most times not related to a VPN connection).

I would like to trigger this script by a daemon like mentioned in this thread. But I would prefer to only monitor the Cisco VPN connection. So my question: Does anybody know, if Cisco AnyConnect VPN client writes/changes a local file, when connecting/disconnecting a VPN connection? So I could trigger my script only on this file.

sdagley
Esteemed Contributor II

@hansjoerg.watzl You could run Composer in Monitor File System mode to watch what happens while connecting/disconnecting with AnyConnect VPN.

tlarkin
Honored Contributor

What are you trying to accomplish from this?

hansjoerg_watzl
Contributor II
What are you trying to accomplish from this?

Two things:
First to have the current VPN IP address, so we can connect with SSH to this Mac without asking/disturbing the user.
And second to create a Smart Group to limit the scope of some of our policies to corporate network only. (Some of our policies/scripts only work internal, so they should not be visible in Self Service, when connected to the Internet only.)
Of course, I could create another EA, which just checks (e.g. ping) our domain controller and use this EA as a SmartGroup. But as we need the Cisco IP address anyway for point 1, this would combine both.

But I see a new challenge with a local script and LaunchDaemon: As I want to write the IP directly to a EA, I need an apiUser and password in my script. But I really don't want the password in clear text in a locally stored script. (Maybe I can create a new apiuser account which has only write permission to EAs and nothing more...no login permission, no read permission for other objects. But I don't know, if this is possible...have to check this first. The other option is to compile the script or develop a small Swift app for this, so the apiuser credentials are not visible anymore.)

tlarkin
Honored Contributor
First to have the current VPN IP address, so we can connect with SSH to this Mac without asking/disturbing the user.

So you allow incoming port 22 via firewall rules on all your Macs?

And second to create a Smart Group to limit the scope of some of our policies to corporate network only. (Some of our policies/scripts only work internal, so they should not be visible in Self Service, when connected to the Internet only.)

This can be done easily in code, or are you wanting to put these in self service and they only show up on network? This means you would have to run a recon every network state change, I guess you could run jamf log which will update the client's IP address in jamf, but it will not calc smart group membership.

This won't scale that great, I would probably just put your flow into code, and build logic to exit if not on network versus trying to fuss with inventory updates in jamf. Also, something that does local system state (like a CM tool) is designed for this sort of stuff, while jamf relies heavily on server side inventory to do these things.

jracosta
New Contributor III

I tend to have best success using /Library/Preferences/SystemConfiguration/preferences.plist.old at a watch path.