Skip to content

Enabling Java in macOS

If you want to use a Java-based app on your Mac, you would think the path forward is obvious: Download and install Java, then run your app.

Well…no. Even now in late 2020, it’s not that easy. Oracle’s Java installer inexplicably doesn’t add the path to Java or set the JAVA_HOME environment variable, so you’ll get an error that you need to install the latest JDK, even though you just did.

So how do you fix it? Googling will yield frustration. There are lots of sites telling you how to fix it, but few of the answers actually work.

The irony is that the fix only takes 2 minutes and is easy. The problem is that it’s hidden at the bottom of the twelfth Stackoverflow discussion you’ll find. Here it is right at the top of this post so you can do it yourself quickly.

Things to know:

  1. You need to edit your ~/.bash_profile file.
  2. This file does not exist by default, even though most posts claim it does.
  3. You need to know the path to java home, and it does not include the version number.


Ok, let’s get down to it. Here are your steps:

  1. Open Terminal.
  2. Enter this command: vi ~/.bash_profile
  3. Press the “i” key to enable insertion mode.
  4. Cut & paste in this line: export JAVA_HOME="/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home"
  5. Press the esc key to leave insertion mode.
  6. Press Z Z to save the file and leave vi.
  7. Enter this command: source .bash_profile
  8. Enter this command to ensure the changes took effect: echo $JAVA_HOME

Your Java apps should now function. Further, because you didn’t change your path and/or use a specific Java version, you should be able to install Java updates and not lose functionality. Also, this should work across all versions of macOS.

Enjoy!

Advertisement

Whitelisting “Unsecure” Websites in Recent Versions of Chrome for macOS

The latest versions of Chrome (from v57 forward I believe) perform extra checking on websites. This may lead you to encounter “Your connection is not private” and “NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED” warnings when visiting sites that are known safe.

Example:

ERR_CERTIFICATE_TRANSPARENCY_REQUIRED

What generates the alert is that not all companies (mine included) implement our web certificates the way Chrome now expects. Something to do with Chrome and OpenSSL using different trusted root CA’s which makes the trust chain slightly different. To be fair, Chrome now checks this to prevent spoofing and MIM attacks, but it results in false errors and causes Chrome to stop offering to store passwords. Annoying.

Googling how to stop these errors yields many red herrings but few actual results. For example, Chrome v53 did this to many users but v54 fixed it, and you’ll trip over this while searching, as well as Android and Linux posts that don’t help. This post is my attempt to document the actual solution for others who are searching for it.

To whitelist a domain in Chrome, ensure the com.google.chrome.plist file in /Library/Preferences contains:

<key>CertificateTransparencyEnforcementDisabledForUrls</key>
<array>
  <string>[your.domain]</string>
  <string>[your.domain2]</string>
  <string>[your.domain3]</string>
</array>

You can put as many or as few entries in the array as you like.

Note: Before you try to view com.google.chrome.plist, be aware it is in binary mode. Use this command to convert it to human-readable format:

plutil -convert xml1 com.google.Chrome.plist

The AuthNegotiateDelegateWhitelist key should be persistent and survive Chrome updates. The plist conversion to xml will not.

Debugging Jamf Pro (née Casper Suite) Self Service app

If your Self Service app is misbehaving, you might be told by your Jamf rep. that there is no debug mode for the app. However, there is. Do this:

sudo defaults write ~/Library/Preferences/com.jamfsoftware.selfservice debug_mode -boolean YES

Quit and restart Self Service.

Then look for the log in ~/Library/Logs/JAMF/. It will be named “JAMFApplications.log”.

To make use of the info in the log, cut & paste the URL from the log into Safari. See if you get different results. We did. Safari has a different timeout than the Self Service app, so our slow-loading Self Service page did finally load in Safari. This told us our problem was a slow JSS, not a non-functioning Self Service app or non-functioning LDAP server.

The .pkg File Format Has an 8Gb Size Limit

I just spent the better part of a day trying to figure out why Composer (part of the Casper Suite) was failing to output a .pkg file. Composer is usually rock solid; it’s the best part of Casper Suite and the industry leader in OS X packaging. Stephan Sudre’s Packages is good too, but Composer has an ease-of-use and feature set that is unrivaled.

So when it failed to create my .pkg I was at a bit of a loss as to how to troubleshoot. I tried it on a 2nd and then a 3rd Mac. Three different OS’s, three different versions of Composer. All failed.

Then I tried Packages. It too failed. This pointed the finger at my contents. One of the files was an 8.7Gb .dmg that had been output by AutoDMG. I tried renaming the file. No go. Then, at the suggestion of JAMF support, I tried having Composer output a (Casper-specific*) .dmg instead of a .pkg.

That was it. Now everything pointed to .pkg not being able to handle the large file I was stuffing into it.

Persistent Googling yielded this obscure reference in the Munki documentation that .pkg files can’t be larger than 8Gb. Ok, that’s not exactly correct. The default max. size for a .pkg is 8Gb if no changes are made to the “distribution.dist” file. Thing is, most packaging tools don’t let you alter this value.

Why was this so hard to figure out? Because when you hit the limit, no error is generated. Neither Composer nor Packages knew they were failing. They simply output a .pkg that didn’t contain everything I’d instructed them to include. There were no logs to view, no clues to follow.

So keep your .pkg files below 8Gb, or, if the file is for use in Casper Suite (see below), use .dmg format. If you’re distributing operating systems, modularize to keep sizes smaller. Everyone will be happier.

* Composer-created .dmg files are of a JAMF-specific file format. They work in Casper Suite but not in any other utility that handles .dmg files. Have Composer output .pkg format if you need to interact with anything outside of Casper.

Auto-run VMWare VM’s On Boot

We had a request to deploy two dozen VM’s to datacenters running on headless Mac Pros. If the Pros ever got shut down or rebooted, the VM’s needed to come back up automatically without requiring anyone to log in to the Mac Pros. Here’s how we did that:

You would think you could create a .plist that runs VMWare using the vmrun command:

<?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.yourcompanyhere.vmware</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/logger "=================================="</string>
<string>/usr/bin/logger "Trying to start vmware VM's now..."</string>
<string>/Applications/VMware\ Fusion.app/Contents/Library/vmrun -T fusion start "/Users/Shared/v1.vmwarevm/v1.vmx" gui</string>
<string>/Applications/VMware\ Fusion.app/Contents/Library/vmrun -T fusion start "/Users/Shared/v1.vmwarevm/v2.vmx" gui</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/jpmcvmware.log</string>
<key>StandardErrorPath</key>
<string>/var/log/jpmcvmware.log</string>
</dict>
</plist>

However, launchd only allows one argument per “ProgramArguments” key. Thus you need to move your commands to a script, and have the .plist reference that script. So do this:

<?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.yourcompanyhere.vmware</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Scripts/vmwarescript.sh/string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/jpmcvmware.log</string>
<key>StandardErrorPath</key>
<string>/var/log/jpmcvmware.log</string>
</dict>
</plist>

Deploy this .plist to the Mac Pros into the /Library/LaunchDaemons directory. Ensure it has 644 permissions.
Then create the script that the .plist references:

#!/bin/sh

/usr/bin/logger "Trying to start vmware VM's now..."
/Applications/VMware\ Fusion.app/Contents/Library/vmrun -T fusion start "/Users/Shared/v1.vmwarevm/v1.vmx" gui
sleep 5
/Applications/VMware\ Fusion.app/Contents/Library/vmrun -T fusion start "/Users/Shared/v2.vmwarevm/v2.vmx" gui
sleep 5

Save the script in the path you set in the .plist above (/Library/Scripts/vmwarescript.sh).

Notes:

  • The VM’s are located in a central location, not in any particular user’s home directory.
  • The VM’s are set to run with a GUI. If you never want your users to access the host Mac’s Virtual Machine Library, change “gui” to “nogui”. The VM’s will still be accessible over VNC and ssh.

Finally, enable the launchd event with the command:

launchctl load /Library/LaunchDaemons/com.yourcompany.here.plist

Most recent users on your Mac, the “asl” directory, and permissions

If you want to find out who the most recent users on your Mac have been, there are ways to do that. Maybe you want to see if someone has been using your Mac. Or how many times you’ve logged in. Or, if it’s your employer’s Mac, who has been using it. You might also want to reliably know who the current user is. (Hint: “whoami” is unreliable, for if you’re in as sudo or root, it will report that, not the current console user.)

The typical way to scrape the current and most recent users list is the command “last -t console”. You will find many websites saying this is definitively the way to find this information, and that if you wanted to pore over that data manually, it’s in /var/log/wtmp.

Not so.

First, you will find that a non-admin user in OS X will get no output from the “last -t console” command.

And when you go poking around wtmp, you’ll find it empty.

You see, Apple moved away from using wtmp after Mac OS X 10.4. That was a while ago. But many websites claim to this day that it’s still in use.

Where did Apple move this information? To /var/log/asl. That’s a directory containing binary files with all the login information you are seeking.

The problem? It’s restricted with 600 permissions. This is why “last -t console” fails for non-admin users.

As you can guess the solution here is easy; relax those permissions. Do a “chmod -R 666 /var/log/asl”. And bingo, your users can run the “last” command without issue.

Unenrolling a Mac from your Casper Suite JSS

We have two JSS’s, one for Development/Testing and one for Production. We want to be able to swap our test Macs back & forth as needed. Unfortunately, JAMF doesn’t provide an “unenroll” switch for the jamf command line tool. You can “removeFramework” but that deletes everything JAMF-related from the workstation so you cannot enroll programmatically and are stuck obtaining a new QuickAdd package and running from the GUI. So I wrote a script to get around that.

There are only two files you need to preserve prior to removing the JAMF framework so that you can re-enroll later:

/Library/Preferences/com.jamfsoftware.jamf.plist
/usr/sbin/jamf

Once those are saved, you’re good to roll. Here is the script:

#!/bin/sh
# January 17, 2014, Kurt Tappe
# This script will swap JSS'

# Set server addresses
prod="https://your.server.here:8443/"
dev="https://your.otherserver.here:8443/"

# Which JSS are we running from?
jss=`/usr/sbin/jamf checkJSSConnection | grep https | cut -d "/" -f3`
if [ "$jss" = "" ]; then
   echo "Error: JSS could not be determined. You may need to first enroll this Mac in a JSS using QuickAdd."
   exit
elif [ "$jss" = "your.server.here:8443" ]; then
   echo "Currently connected to $jss. Switching to Dev..."
   newjss=$dev
else
   echo "Currently connected to $jss. Switching to Prod..."
   newjss=$prod
fi

# Preserve prefs & binary
cp /Library/Preferences/com.jamfsoftware.jamf.plist /Library/Preferences/com.jamfsoftware.jamf.backup
cp /usr/sbin/jamf /usr/sbin/jamfbackup

# Disconnect
echo "Removing framework..."
/usr/sbin/jamf removeFramework
sleep 5

# Restore old prefs and modify them with the new JSS URL
echo "Restoring prefs & enrolling..."
cp /Library/Preferences/com.jamfsoftware.jamf.backup /Library/Preferences/com.jamfsoftware.jamf.plist
cp /usr/sbin/jamfbackup /usr/sbin/jamf
defaults write /Library/Preferences/com.jamfsoftware.jamf allowInvalidCertificate -boolean true
defaults write /Library/Preferences/com.jamfsoftware.jamf jss_url -string $newjss
defaults delete /Library/Preferences/com.jamfsoftware.jamf last_management_framework_change_id

# Reconnect
/usr/sbin/jamf enroll -noRecon

One thing required for this to work is for you to not be forcing the use of certificates. Yes, we use them, but for ease of management we don’t require them for enrollment, so that we can do things like run these scripts.

Hope this helps,
-Kurt

Programmatically Scraping Data From your Casper Suite JSS

So let’s say you’re writing a bash script and need to glean data from your JSS about a particular Mac workstation. In this case, you have the workstation name and you want to know the Mac’s IP address. Well, JAMF has provided a means to do that via the REST API.

If you contact JAMF, they’ll show you a way to obtain info about any Mac workstation in the JSS if you already know its computer ID in the JSS:

curl -v -u USERNAME:PASSWORD https://JSS.URL:8443/JSSResource/computers/id/###/subset/general -X GET | xpath //computer/general/ip_address | sed -e 's/\<ip_address>//g; s/\<\/ip_address>//g'

This is hardly useful though, as who knows each workstation’s ID? (The ID happens to be pretty much random; it’s a chronological increment based on when the Mac was enrolled in the JSS. Your 9th Mac is “9”, your 47th is “47”, etc.)

The other problem is that both ‘curl’ and ‘xpath’ are very verbose in their output. If you’re writing a command-line utility, you don’t want them mucking up the user’s screen. Luckily there’s a better way.

curl -v -u USERNAME:PASSWORD https://JSS.URL:8443/JSSResource/computers/name/NAME/subset/General | grep -Eo '<ip_address>[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' | cut -d ">" -f2

(Note: I attempted to use ‘sed’ instead of ‘grep’ & ‘cut’ above, but ‘sed’ just wouldn’t parse the tag-laden output. Ping me if you have a solution.)

In both examples above, you of course need to substitute your authentication into USERNAME and PASSWORD, your JSS address into JSS.URL, and in the bottom example the workstation name you’re referencing into NAME.

If you try the commands directly in Terminal you’ll see the entire transaction with the server, but don’t worry; if you pipe the output to a file or use it in a full script, the output will be clean.

I’ll post more code snippets as I develop them. Specifically, I’m trying to do the reverse of the above; take an IP address and convert it into a workstation name or (better yet) serial number. Watch this space.

Error While Updating Enterprise-packaged Adobe Creative Suite

If you’re trying to install updates or upgrades to Adobe Creative Suite, you may run across a refusal of the updater to run. The error will resemble:

Update failed, updates have been suppressed by the Administrator

Funny thing is, you actually are the likely cause of this error. Remember back when you were packaging Creative Suite using Adobe Application Manager Enterprise Edition (AAMEE)? Near the end of the series of dialog boxes you went through, you clicked “Prevent Updates”.

And there we are. Adobe remembered this and is continuing to suppress updates. It doesn’t care if you’re the admin; updates are suppressed!

The fix is easy even if the path to it is long. What you need to do is edit or delete this file:

/Library/Application Support/Adobe/AAMUpdaterInventory/1.0/AdobeUpdaterAdminPrefs.dat

Removing it is draconian but quick & easy. All you need gone / changed is the line within it that reads:

 <Suppressed>"1"</Suppressed>.

Make the “1” a “0” or just delete that line.

This is all documented by Adobe here, but Googling or Binging the subject is often fruitless. So I’m reposting here to make it easier for all to find.

Keeping 10.x Updates

Update April 2015: I was able to retain a copy of the Yosemite 10.10.3 Beta update via this same method. The trick is the updates do get erased if you let App Store install and reboot. You generally have 60 seconds after the download completes and before Finder shuts down to locate the update file and copy or duplicate it.

Back before all Apple updates came to be distributed through the App Store, it was easy to retain software updates. You just told Software Update app to Keep Updates through its menu system.

There are distinct advantages to retaining updates instead of redownloading them every time. Bandwidth, which Apple seems to think we all have in abundance, is a large one, especially considering many updates are measured in gigabytes these days. But perhaps you also want to keep an update for use in AutoDMG, InstaDMG, Munki, or Casper Suite.

With the advent of App Store, these updates are notably harder to retain. But if you’re quick, you can still keep them. Follow these steps:

1) Initiate the download of the update using App Store.

2) Once the download is complete, look in /Library/Updates/[random #]

OK, the number probably isn’t random, but you’re unlikely to find out Apple’s numbering system, so just look at the most recently updated numerical folder inside /Library/Updates. Your .pkg will be there waiting for you.