Posted on 05-03-2016 09:10 AM
Hi All,
I'm pretty sure I know the answer to this... but thought I'd reach out to see if there's anything I'm missing or if anybody has a creative solution. Our AD policy disables machine accounts that haven't checked in within 30 days. This causes problems for our remote users as they often bump into this limit as they need to connect to VPN in order to check-in. A lot of users do not need VPN for their day to day work so they only find out when it's time for a password change.
I'd love for the JSS to notify AD that a machine actively checking into the JSS is still "active". Or conversely, to have AD check the JSS for checkins and act accordingly.
Crazy? Impossible? Both?
Thanks!
Matt
Posted on 05-03-2016 09:32 AM
Not crazy, but likely impossible. There's no way I can think of for the JSS to pretend or masquerade as a computer checking in with AD to make it believe its still an active system.
That being said, you may have a few other options.
I think many environments are changing the passinterval value on their Macs using dsconfigad to 0 (disabled) or setting it out very far so the Mac doesn't flip machine passwords as frequently. However, if its actually an AD policy disabling them, that may not help if AD itself is doing the disabling of the machine accounts from its end. Your security folks may not agree to doing that anyway.
Another option would be deploy a custom LaunchAgent and script that can periodically check for AD (see if its on the internal network) and check the last machine logon time from AD, and write it to a local hidden file. Each time the script runs, if it can't connect to AD, it can calculate how many days its been since it was last able to communicate using date commands and alert the user once it starts getting close to that 30 day expiration time and encouraging them to get on VPN, thus, hopefully preventing their machine from falling out of domain membership. OTOH, if it can connect to AD, it can pipe the new logon date into the file and start the calculation from that timeframe. IOW, it will reset the clock.
You can have the LaunchAgent get called by monitoring for network changes and also run maybe once an hour or so.
And I'm sure there are more options than these.
Posted on 05-03-2016 09:44 AM
Hi,
Thanks for the reply. Yep, I'm thinking a script of sorts that notifies users will be my only recourse for this one. :) I'll play around with this for a bit and post back here with what I have. Might want some feedback!
Thanks,
Matt
Posted on 05-03-2016 01:04 PM
Sounds like a good case for Apple Enterprise Connect.
Posted on 05-03-2016 02:29 PM
@Berrier Good call - thanks for the reminder!
Posted on 05-04-2016 02:32 PM
My thoughts would be to go the other way, use an external script checking the last applicable date on JSS and disabling accounts that have not contacted the server for 30 days.
If not all computers contact JSS then your sys admin can add your JSS registered computers to a security group or OU (either will work) that prevents or denies access to the policy that disables the account after 30 days.
Regards
Graeme
Posted on 05-10-2016 07:40 AM
I've been working on a script the last few days and finally have it mostly working... but then I had a thought! Could this be an EA instead? This would get around the difficulty in trying to get the "last checkin" from AD if the user is not connected to the domain. I could then run a policy based on the EA, probably simplifying this a ton! Thoughts?
@Graeme Sadly, my AD environment is pretty much unchangeable, so it's up to me to work around the "issues"... :)
Posted on 05-10-2016 07:45 AM
wait... EA's run from the local machine right - not the server. darn. That makes it more difficult...
Posted on 05-10-2016 08:15 AM
Yes, EAs get downloaded (sort of) from the JSS during an inventory collection and run locally on the machine, and then update the data, along with the rest of the inventory information, back to the JSS' computer record.
Posted on 05-10-2016 08:56 AM
This may come in handy ?
I use the password expiry check from Peter Bukowinski and @bentoms ADPassMon as an Extension Attribute to report the password expiry, though this will only update when the inventory runs and the Mac is on the VPN or our network.
You could maybe add a check in the script so it only runs when your network is reachable and then write the expiry result to a local file that can then be read by the JSS, then make a smart group/policy and notificaton for the users ?
#!/bin/bash
# This section uses python to get the current console user. You can
# usually just use the built-in $USER variable, but this is more
# robust.
USER=$(/usr/bin/python -c
'from SystemConfiguration import SCDynamicStoreCopyConsoleUser;
print SCDynamicStoreCopyConsoleUser(None, None, None)[0]')
# This looks up the password expiration date from AD. It's stored
# in a numerical format that we have to convert.
xWin=$(/usr/bin/dscl localhost read /Search/Users/$USER
msDS-UserPasswordExpiryTimeComputed 2>/dev/null |
/usr/bin/awk '/dsAttrTypeNative/{print $NF}')
# This converts the MS date to a Unix date.
xUnix=$(echo "($xWin/10000000)-11644473600" | /usr/bin/bc)
# This gives us a human-readable expiration date.
xDate=$(/bin/date -r $xUnix)
# This gives us a Unix date value for right now.
today=$(/bin/date +%s)
# This uses some simple math to get the number of days between
# today and the date the password expires.
xDays=$(echo "($xUnix - $today)/60/60/24" | /usr/bin/bc)
# Show the results
#echo "Username: $USER"
#echo "Expiration date: $xDate"
echo "<result>$xDays</result>"
Posted on 05-10-2016 09:00 AM
@May, that's a good script, but I think @mbezzo is looking for a way to check on the Mac's AD expiration, not the user's. These aren't the same thing. My user account may not expire for months, but if my AD joined Mac isn't in contact with a DC in our organization for about a 30 day stretch, the AD join breaks, even though my AD account is still perfectly valid.
Posted on 05-10-2016 09:11 AM
Posted on 05-10-2016 11:26 AM
@May No worries! Appreciate ya stopping by and helping out. Yep, @mm2270 is correct - it's the machine account we're struggling with. I'll definitely check to see if it stored locally somewhere. That might make a few steps easier. Here's what I have so far. Would love ideas on how to deal repeat reminders to the user. Basically, if the computer account expires in 7 days or less, I pop up a dialog asking the user to connect to VPN, or an "ok, i'll do it later" button. I'd like the "do it later button" to mute checks for a day or two, and then resume popping up. Any good ideas on the best way to do this? I plan to have this script regularly run with a LaunchAgent, but yeah, I don't want to kill the users with pop ups (Well... I do.. but :) ) Apologies for the number of variables - this somehow makes sense to me! :)
#!/bin/sh
#####################################################################################################
#
# NAME
# ADLastLogonCheck.sh
#
# SYNOPSIS
# Checks the dscl attribute "lastLogonTimestamp" to see if it's close to 30 days ago. If it is,
# prompt user to connect to VPN for a bit to get updated.
#
####################################################################################################
#
# HISTORY
#
# Version: 1.0
#
# - Matt Bezzo, 27.04.2016
#
####################################################################################################
########################################
############## Variables ###############
########################################
loggedInUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`
# yes/no variable for if there's connectivity to the domain
corpNetAvailabe=""
# Set to the name of your domain (All Caps)
domain="YOURDOMAIN"
# Sets the current machine name
computerName="`scutil --get ComputerName`"
# current time stamp directly from AD. Setting it to zero so we can check to see if it has a "real" value later
ADlastLogonTimestamp="0"
# Path to the text file that stores the last logon time stamps for retrieval if no access to AD
pathToADLastlogintxt="/Library/Application Support/yourcompany/ADLastlogin.txt"
# The latest entry from the Time stamp file
lastLogonTimestampFromFile=""
currentTimeUNIX=$(date +%s)
########################################
############## Functions ###############
########################################
log(){
NOW="$(date +"%Y-%m-%d %H:%M:%S")"
echo "$NOW": "$1"
}
#### Check for route to your.domain.com
corpNetCheck(){
echo "Checking a ping to your.domain.com"
ping -c 1 your.domain.com
if [ "$?" != "0" ]; then
log "Unable to reach your.domain.com."
corpNetAvailable="no"
elif [ "$?" == "1" ]; then
log "your.domain.com is reachable."
corpNetAvailable="yes"
fi
}
displayVPNReminder(){
# $1 = window title
# $2 = prompt text
su - "${loggedInUser}" -c osascript -e <<EOT
tell application "System Events"
with timeout of 8947878 seconds
set result to (display dialog "$2" with title "$1" with icon ("/Library/Application Support/yourcompany/logo.icns" as POSIX file) buttons {"Open Edge Client", "Okay, I'll do it soon!"} default button "Open Edge Client")
end timeout
end tell
if button returned of result = "Open Edge Client" then
return "Yes"
else
return "No"
end if
EOT
}
########################################
########## Begin Main Program ##########
########################################
# Logging for troubleshooting - view the log at /var/log/ADLastLogonCheck.log
touch /var/log/ADLastLogonCheck.log
exec 2>&1>/var/log/ADLastLogonCheck.log
# Create ADLastlogin text file if it doesn't already exist
if [ ! -f "$pathToADLastlogintxt" ]; then
touch "$pathToADLastlogintxt"
fi
# Check for corpNet access
corpNetCheck
# Write the latest Logon timestamp to the text file if we have domain access
if [ "$corpNetAvailable" == "yes" ]; then
ADlastLogonTimestamp="`dscl /Active Directory/$domain/All Domains/ -read /Computers/$computerName$ | grep lastLogonTimestamp | cut -c 38-`"
echo "$ADlastLogonTimestamp" >> "$pathToADLastlogintxt"
fi
# Check the time stamp variable to see if it has a valid value, otherwise read the last time stamp from the text file and set it as the ADlastLogonTimestamp value
if [ "$ADlastLogonTimestamp" == "0" ]; then
lastLogonTimestampFromFile=`tail -1 "$pathToADLastlogintxt"`
ADlastLogonTimestamp="$lastLogonTimestampFromFile"
fi
# Now that we have the most recent lastLogonTime, convert it to Unix time for calculations
ADlastLogonUNIX=$((($ADlastLogonTimestamp/10000000)-11644473600)) #disable for testing
#hard coding value for testing
#ADlastLogonUNIX=1461525577
# Get the UNIX timestamp for the computer expiration date (ADlastLogonUNIX + 30 Days)
computerExpirationDateUNIX=$(($ADlastLogonUNIX+2592000))
# Convert computerExpirationDateUNIX to human readable so we can tell the user when the account expires
computerExpirationDate=$(date -r $computerExpirationDateUNIX "+%T on %B %d")
# Find out how many days ago from today the computer checked in
timeFromLastADCheckIn=$(($currentTimeUNIX-$ADlastLogonUNIX))
# We want to notify the user 7 days before their computer account expires. Since the oldest possible "current" ADlastLogonTimestamp can be up to 14 days old,
# we add 9 days to it (777600) and then check if that's greater than or equal to 23 days (1987200) which is 7 days before the 30 day expiration.
if [ "$(($timeFromLastADCheckIn+777600))" -ge "1987200" ]; then
VPNReminderAnswer="$(displayVPNReminder 'Please connect to VPN soon!' "Your computer account will expire at:
$computerExpirationDate.
Please connect to VPN at your earliest convenience and leave it connected for awhile to prevent your account from being deactivated.")"
if [ "$VPNReminderAnswer" == "Yes" ]; then
open /Applications/BIG-IP Edge Client.app
elif [ "$VPNReminderAnswer" == "No" ]; then
#set next reminder date +24hours somehow?
echo "no"
fi
fi
Posted on 05-10-2016 11:31 AM
Also, I'm still working out how to handle the inevitable situation where there is no domain connection OR file storing a date. Any ideas for that would be welcomed as well!
Thanks,
Matt
Posted on 05-10-2016 11:38 AM
FWIW, if you can somehow pull the Date Modified for the keychain item /Active Directory/DOMAIN that will show you the last time the computer password was changed.
I have the following EA to check to see if the domain talking is working: You will want to fill in the KNOWN_USERNAME, KNOWN_UUID and DOMAIN with variables that work for your domain.
#!/bin/bash
# AD Bind Check Extension Attribute
adName=`dsconfigad -show | grep "Computer Account" | awk '{print toupper}' | awk '{print $4}' | sed 's/$$//'`
if [ ! "$adName" ]; then
echo "<result>Not Bound</result>"
exit 0
else
result1="Bound as $adName"
fi
ldapTest=`id KNOWN_USERNAME | grep KNOWN_UUID`
if [ ! "$ldapTest" ]; then
result2="LDAP Query Failed"
else
result2="LDAP Query OK"
fi
keychainTest=`security find-generic-password -l "/Active Directory/DOMAIN" /Library/Keychains/System.keychain`
if [ ! "$keychainTest" ]; then
result3="AD Password Missing"
else
result3="AD Password OK"
fi
echo "<result>$result1 - $result2 - $result3</result>"