How To: Package a Docker Installer that Does Not Request Admin Privileges

Jacher
New Contributor II

b9c261957662430aa13822579cd4cd8b

When Docker installs on a machine, it asks the user for root privileges to finish its installation process after you start it the first time. In our environment, we decided that it's best users don't need to enter any sort of credentials to finish the installation of an application. So, via a trail of troubleshooting steps I found online and some trial & error, I was able to put together an installer that does not need root privileges to finish its installation.

Use the following directions to create a version of the installer that does not require Admin Rights. If you are already familiar with making JSS policies and using Composer, check out the TLDR section at the bottom.

  1. Download Docker For Mac
  2. Mount the Docker DMG
  3. Open Composer
  4. Click on New
  5. Select Normal Snapshot
  6. Click Next
  7. Name the Snapshot and click next
  8. Once the Snapshot has finished creating, drag the Docker application into the root Applications folder
  9. Open Applications, and then open Docker
  10. Docker will post an introductory window, click OK
  11. Docker will ask for privileged access, click OK
  12. Enter your Admin credentials and click okay
  13. Docker will run its final installation steps
  14. Click Got It after Docker has finished running its post-installation d14f28b4a41743ba9355c5e2a24870db
  15. In Composer, click Create Package Source
  16. Drill down Library>LaunchDaemons
  17. Select the com.docker.vmnetd.plist file
  18. Ensure that the file's owner is root, and group is wheel.
  19. Check the box for X (execute) on the Owner row. Verify that it states Mode: 744 (not 644) e2159ca02bba4e98bf4f7525189a309f
  20. Drill down Users>{username}>Library>Containers>com.docker.docker
  21. Delete the Data folder within the com.docker.docker folder afb33a1bf1e444da8c8e4d247841f592
  22. Ensure there are no additional folders unrelated to the Docker installation in the package source. In my case I removed the Saved Application State folder.
  23. Once you've done this, click on Build as DMG and save to your package build location
  24. Open Casper Admin and drag the DMG into Casper Admin to upload.
  25. Change the settings on the DMG to turn on FEU (Fill Existing User Template)
  26. Categorize the file
  27. Save
  28. Log into the JSS and Create the Docker policy.
  29. Place the DMG in the policy
  30. Set the policy to restart after install (Restart Options>User Logged In Action> Restart)
  31. Add Files and Processes, add the following one liner to Execute Command: /bin/launchctl load -Fw /Library/LaunchDaemons/com.docker.vmnetd.plist
  32. Setup to install from Self Service

TLDR Version;
Install using a composer snapshot as normal.
The only major differences are as follows:
Change /Library/LaunchDaemon/com.docker.vmnetd.plist to 744 and root:wheel
Delete the Data folder found in Users/{username}/Library/Containers/com.docker.docker
Save as DMG, place on Casper Admin with FEU enabled
In Policy: Ensure that the computer restarts after install. Add the following one liner in Execute Command:
/bin/launchctl load -Fw /Library/LaunchDaemons/com.docker.vmnetd.plist

57 REPLIES 57

which version of Docker are you using for this script ? 
I am using composer to build and deploy our package. 
basically, I copy the dmg to a tmp directory 

mount the dmg using  hdiutil -attach <path to dmg> -nobrowse
# remove an exsiting version of the app
/Applications/Docker.app/Contents/MacOS/uninstall
rm -rf /Applications/Docker.app
cp -R /Volumes/Docker/Docker.app /Applications/Docker.app
hdiutil unmount /Volumes/Docker   (should I use hdiutil -detach /Volumes/Docker here ?)
/usr/binxattr -dr "com.apple.quarantine" /Aplications/Docker.app 2> /dev/null

( now i should switch to the logged on user ? ) 
# I have a runasUser function 
runAsUser() 
{ 
        if [[ "${currentUser}" != "loginwindow" ]]; then
            launchctl asuser "$uid" sudo -u "${currentUser}" "$@"
        else
            echo "no user logged in"
            exit 1
        fi
}
currentUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' )
uid=$(id -u "${currentUser}")
/Applications/Docker.app/Contents/MacOS/install --accept-license --user=$currentUser
#or 
runAsUser /Applications/Docker.app/Contents/MacOS/install --accept-license --user=$currentUser
runAsUser "/Applications/Docker.app/Contents/MacOS/Docker --unattended --install-privileged-components" 
# I am not sure the --install-priveleged-components option exists in version 4.25 ? 


Thank you for any insight for this post install script ...

 

 

 

I may be wrong, but as far as I know using

/Applications/Docker.app/Contents/MacOS/uninstall

"...destroys Docker containers, images, volumes, and other Docker related data local to the machine, and removes the files generated by the application." (See https://docs.docker.com/desktop/uninstall/

So in order to patch Docker I would not run the uninstall script - otherwise your user's databases will be gone.

mm2270
Legendary Contributor III

Just wanted to mention how perfect the --install-privileged-components command mentioned by @Chris_Potrebka was. Saved my day. You get an extra Kudos!

I'm not sure why Docker doesn't seem to document that from what I can tell. If it is documented, it's not easy to find. Though now that I know the term to search for, it seems to show up on a couple of threads on Docker's forum at least.

cpotrebka
New Contributor II

Thanks tones for the kudos! Greatly appreciated. 

P.S. l've consolidated my Jamf accounts down to this my original one. 🙂 

user-iZgcKItEmv
New Contributor

This seems broken again with the latest version of Docker:

Beginning on August 31, 2021, you must agree to the Docker Subscription Service Agreement to continue using Docker Desktop. Read the Blog and the Docker subscription FAQs to learn more about the changes.

Using the --install-privileged-components command doesn't bypass needing to accept the new Service Agreement. 

Any thoughts?

JamieG
New Contributor III

My learned colleague has scripted this and has it working (for initial installs and updates) with the attached script. All users are not admins. Tested and working on Intel and Apple Silicon.

https://gist.github.com/SamStenton/716fb44fae9d59b320a4b92108af0beb

 

#!/bin/bash


if [[ `uname -m` == 'arm64' ]]; then
    # Apple Silicon
    echo 'Downloading Apple Silcon release'
    curl -o ~/Downloads/Docker.dmg https://desktop.docker.com/mac/main/arm64/Docker.dmg
else
    # Intel 
    echo 'Downloading Apple Intel release'
    curl -o ~/Downloads/Docker.dmg https://desktop.docker.com/mac/main/amd64/Docker.dmg
    # curl -o ~/Downloads/Docker.dmg https://desktop.docker.com/mac/main/amd64/72729/Docker.dmg #old version to test updating
fi


# Mount image 
hdiutil attach ~/Downloads/Docker.dmg

# Copy to Applcation folder
rm -rf /Applications/Docker.app # For updates remove the old app
cp -R /Volumes/Docker/Docker.app /Applications

# Install docker privilaged components
/Applications/Docker.app/Contents/MacOS/Docker --unattended --install-privileged-components


# Accept license (doesn't seem to be working)
open -a /Applications/Docker.app --args --unattended --accept-license

# Clean up.
echo 'Cleaning up'
hdiutil unmount /Volumes/Docker/Docker.app 
rm ~/Downloads/Docker.dmg

 

cdev
Contributor III

We've taken a bit of a different approach so as not to make it a live download. We are packaging the docker.dmg with a postinstall script that installs and configures based on the Docker docs. The only weird thing is I have to temporarily disable Gatekeeper or the install will fail:

 

#!/bin/bash

## based on Jamf Nation content:
# https://community.jamf.com/t5/jamf-pro/how-to-package-a-docker-installer-that-does-not-request-admin/m-p/199627

## Docker "Command-line" install
# https://docs.docker.com/desktop/install/mac-install/#install-from-the-command-line

# installed resources in /tmp/docker/*
dockerDMG="Docker.dmg"
mountName="Docker"
currentUser=$( /usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )

#################
# NEED TO DISABLE GATEKEEPER TO INSTALL THIS WAY?!!? Yep. Wow.
/usr/sbin/spctl --master-disable
#################

/usr/bin/xattr -d com.apple.quarantine "/tmp/docker/${dockerDMG}"

echo "Mounting Docker DMG"
/usr/bin/hdiutil attach "/tmp/docker/${dockerDMG}"

echo "DMG attached at /Volumes/${mountName}"
echo
echo "Starting Docker installation"
"/Volumes/${mountName}/Docker.app/Contents/MacOS/install" --accept-license --user="$currentUser"
echo

echo "Setting permissions on Docker.app"
/usr/sbin/chown -R "$currentUser" "/Applications/Docker.app"

echo "Clearing Quarantine Flags"
/usr/bin/xattr -dr com.apple.quarantine /Applications/Docker.app

echo "Installing additional Docker components so users don't need admin rights"
"/Applications/Docker.app/Contents/MacOS/Docker" --install-privileged-components

## Cleanup
/usr/sbin/spctl --master-enable

/bin/echo "Starting cleanup"
echo "Unmounting $dockerDMG"
/usr/bin/hdiutil detach "/Volumes/$mountName"
sleep 5
echo "Removing temp files"
/bin/rm -rf /tmp/docker

exit 0

 

Eric1115
New Contributor II
Thank you! I will give this a try.

Eric1115
New Contributor II

Hi I tried the script and not sure what I am doing wrong but I still can’t get pass the issue I am getting when running docker - -install-privileged-components

i get a pop-up

”Permission erro”

Running Docker Desktop as root is dangerous. Please run it as a regular user.

 

thanks

AquibAS
New Contributor

@Eric1115 did you get a fix for error you mentioned ?

 

DDP
New Contributor

Hi @Eric1115 or @AquibAS, were you able to bypass that root error you've mentioned?

markacorum
New Contributor II

I started getting tickets with the previous Docker install no longer working. I rewrote my script this morning with the following and it seems to be functioning just fine now. I am by no means a scripting pro but it does the job for me. Feel free to offer up any changes. I also did not write the original script, I made adjustments to the one we were using.

#!/bin/bash


if [[ `uname -m` == 'arm64' ]]; then
# Apple Silicon
echo 'Downloading Apple Silcon release'
curl -o ~/Downloads/Docker.dmg https://desktop.docker.com/mac/main/arm64/Docker.dmg
else
# Intel
echo 'Downloading Apple Intel release'
curl -o ~/Downloads/Docker.dmg https://desktop.docker.com/mac/main/amd64/Docker.dmg
fi


# Mount image
hdiutil attach ~/Downloads/Docker.dmg

# Copy to Applcation folder
rm -rf /Applications/Docker.app # For updates remove the old app
cp -R /Volumes/Docker/Docker.app /Applications
/Applications/Docker.app/Contents/MacOS/install --accept-license --user=$3

# Clean up.
echo 'Cleaning up'
hdiutil unmount /Volumes/Docker/Docker.app
rm ~/Downloads/Docker.dmg

#Configure Docker
cp -R /Applications/Docker.app/Contents/Resources/bin /Users/$3/.docker
ln -s -f /Users/$3/.docker/bin/docker /usr/local/bin
ln -s -f /Users/$3/.docker/run/docker.sock /var/run/docker.sock

 

This seems to work if I'm signed in to macOS Ventura but not if the policy is running as root at the login screen.

adjustment to my script due to my offensive security team. Using random temp directory.

 

#!/bin/bash


if [[ `uname -m` == 'arm64' ]]; then
# Apple Silicon
echo 'Downloading Apple Silcon release'
url="https://desktop.docker.com/mac/main/arm64/Docker.dmg"
tmpDir=$(/usr/bin/mktemp -d "/tmp/docker-install.XXXXXX")
curl -s -o "$tmpDir/Docker.dmg" ${url}
else
# Intel
echo 'Downloading Apple Intel release'
url="https://desktop.docker.com/mac/main/amd64/Docker.dmg"
tmpDir=$(/usr/bin/mktemp -d "/tmp/docker-install.XXXXXX")
curl -s -o "$tmpDir/Docker.dmg" ${url}
fi

cd $tmpDir
# Mount image
hdiutil attach Docker.dmg -nobrowse

# Copy to Applcation folder
rm -rf /Applications/Docker.app # For updates remove the old app
cp -R /Volumes/Docker/Docker.app /Applications
/Applications/Docker.app/Contents/MacOS/install --accept-license --user=$3

# Clean up.
echo 'Cleaning up'
hdiutil unmount /Volumes/Docker/Docker.app
rm -rf $tmpDIR

#Configure Docker
cp -R /Applications/Docker.app/Contents/Resources/bin /Users/$3/.docker
ln -s -f /Users/$3/.docker/bin/docker /usr/local/bin
ln -s -f /Users/$3/.docker/run/docker.sock /var/run/docker.sock

DDP
New Contributor

Hi @markacorum, for some reason, I am still having this stubborn issue with getting the --accept-license to work properly. Have you or anyone ever experienced that with your modified script? 

markacorum
New Contributor II
Not that I have seen so far. I have had about 20 users run the install. My assumption is this was changed in the more current version of docker.

Did anything get adjusted script wise? Spaces added?


Thanks,

MARK CORUM | sr SYSTEMS ENGINEER

CELL : 402-213-7633

Mark.Corum@earlywarning.com

www.earlywarning.com<>



[signature_1859496312]

This email transmission may contain confidential and/or private information, which is the property of the sender. The information in this email or attachments thereto is intended for the attention and the use only of the addressee. If you are not the intended recipient, you are hereby notified that any disclosure, copying, or distribution of the contents of this email transmission, or the taking of any action in reliance thereon or pursuant thereto, is strictly prohibited. Should you have received this email in error, please contact the sender and delete and destroy all copies of the original message

DDP
New Contributor

Hey @markacorum, no changes were made, I simply used your modified script and unfortunately, the agreement window keeps popping back up post install. I have a sneaky suspicion that it's because the flag doesn't run properly if invoked while the dmg is still mounted. Going to try to unmount first and see what happens. Would love to hear if you or anyone experiences the same thing.

DDP
New Contributor

This is the script I have been using and does bypass requesting admin priv.

 

#!/bin/bash

# Function to get the latest download URL for Docker
get_latest_Docker_url() {
# Replace the following line with a command or script to fetch the latest download URL
# For example, you might use curl or wget to get the download link from the Docker website
# Example: LATEST_URL=$(curl -s https://example.com/Docker-latest-url)
LATEST_URL="https://desktop.docker.com/mac/main/amd64/Docker.dmg?utm_source=docker&utm_medium=webreferral&utm_ca..."
echo "$LATEST_URL"
}
# Grab the username of the user that last logged in (current user).
currentUser=`defaults read /Library/Preferences/com.apple.loginwindow lastUserName`

# Function to download and install the latest version of Docker
install_Docker() {
# Get the latest download URL
Docker_URL=$(get_latest_Docker_url)

# Download the latest version
curl -L -o "/tmp/Docker-latest.dmg" "$Docker_URL"

# Mount the cached Docker .dmg
hdiutil attach /tmp/Docker-latest.dmg

# Install the application
"/Volumes/Docker/Docker.app/Contents/MacOS/install" --user="$currentUser" --accept-license

# Unmount the DMG
hdiutil detach "/Volumes/Docker"

# Install additional Docker components so users don't need admin rights
su "$currentUser" -c "/Applications/Docker.app/Contents/MacOS/Docker" --unattended &
su "$currentUser" -c "/Applications/Docker.app/Contents/MacOS/Docker" --install-privileged-components &

# Cleanup the temporary files
rm "/tmp/Docker-latest.dmg"

echo "Docker has been installed successfully!"
}

# Run the installation function
install_Docker