Slides from the “Running Jamf Pro at Scale, from SAP with ❤️” session at Jamf Nation User Conference 2022

For those who wanted a copy of my scaling talk at Jamf Nation User Conference 2022, here are links to the slides in PDF and Keynote format.

PDF: https://tinyurl.com/jnuc2022pdf
Keynote: https://tinyurl.com/jnuc2022key

Un article par semaine : c’est fini

Cela fait 6 ans que j’écris un article chaque semaine sur mon blog. J’ai décidé d’arrêter de faire ces mises à jour régulières. Tout d’abord parce que mon objectif initial est atteint. Ensuite, parce que j’ai d’autres priorités aujourd’hui que je n’avais pas en 2016. Je continuerai d’alimenter ce blog de temps en temps, mais ce ne sera plus aussi régulier.

Microsoft Defender tamper protection status detection for Jamf Pro

As a follow-up to my earlier post about working with Microsoft Defender’s tamper protection, I’ve written an Extension Attribute for Jamf Pro which detects and reports on Defender’s tamper protection status. For more details, please see below the jump.

The Extension Attribute uses Defender’s mdatp command line tool to report on Defender’s tamper protection status. Once the mdatp tool is verified to be installed and executable, it’s used to check the tamper protection status. The EA will return one of the following values:

  • 000
  • 001
  • 010
  • 100

The returned values indicate the following:

  • 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
  • 001 = Tamper protection is fully disabled.
  • 010 = Tamper protection is set to audit mode.
  • 100 = Tamper protection is fully enabled.

The Extension Attribute is available below. It’s also available from GitHub using the following link:

https://github.com/rtrouton/rtrouton_scripts/blob/main/rtrouton_scripts/Casper_Extension_Attributes/check_microsoft_defender_tamper_protection_status


#!/bin/bash
# Check to see if Microsoft Defender's tamper protection is enabled.
# This Jamf Pro Extension Attribute will return one of four statuses
#
# 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
# 001 = Tamper protection is fully disabled.
# 010 = Tamper protection is set to audit mode.
# 100 = Tamper protection is fully enabled.
mdatpPath="/usr/local/bin/mdatp"
# Set default result for the Extension Attribute to be the following:
#
# 000 = The /usr/local/bin/mdatp command-line tool cannot be found or is not executable.
eaResult="000"
# Verify that the following tool is installed and executable:
#
# /usr/local/bin/mdatp
if [[ -x "$mdatpPath" ]]; then
# If the mdatp tool is installed, Defender's tamper protection
# status is checked by running the following command:
#
# /usr/local/bin/mdatp" health –field tamper_protection
#
# There are three possible keywords that can be returned by this command:
#
# disabled – tamper protection is completely off.
# audit – tampering operations are logged, but not blocked.
# block – tamper protection is on, tampering operations are blocked.
tamper_protection_enabled="$("$mdatpPath" health –field tamper_protection | awk -F'"' '{print $2}')"
if [[ "$tamper_protection_enabled" = "disabled" ]]; then
eaResult="001"
elif [[ "$tamper_protection_enabled" = "audit" ]]; then
eaResult="010"
elif [[ "$tamper_protection_enabled" = "block" ]]; then
eaResult="100"
fi
fi
echo "<result>$eaResult</result>"
exit 0

view raw

gistfile1.txt

hosted with ❤ by GitHub

Jamf Pro 10.41.0 and SSL verification alerts

Following an upgrade to Jamf Pro 10.41.0, you may notice that you have an alert showing in the Jamf Pro admin console.

Screen Shot 2022 09 12 at 10 39 33 AM

When you click on the alert, you will see the following alert notification.

Verification of SSL certificates is disabled.

There will be a link to enable SSL certificate verification.

Screen Shot 2022 09 12 at 10 39 51 AM

If you click that link, it’ll take you to Management Settings: Computer Management – Management Framework: Security.

Screen Shot 2022 09 09 at 10 15 26 AM

So now what? For more details, please see below the jump.

The SSL certificate in question is the SSL certificate used by Tomcat. Jamf is deprecating the use of self-signed certificates for Tomcat, as mentioned in the Jamf Pro 10.41.0 release notes:

Removal of unverified SSL certificates in Jamf Pro — In a future release of Jamf Pro the option to use an unverified SSL certificate for Jamf Pro will be removed. Customers with Cloud-hosted environments and those with a verified third-party certificate will see no changes. Customers with On-Premise environments using Jamf Pro’s built-in certificate authority to issue SSL certificates need to move to a trusted third-party certificate.

Screen Shot 2022 09 12 at 10 44 05 AM

The alert is being triggered if you have the SSL Certificate Verification setting set to one of the following:

  • Disabled
  • Always except during enrollment

The Disabled setting means the Jamf Pro agent installed on a Mac isn’t verifying certificate trust at all for the SSL certificate that Tomcat is using.

The Always except during enrollment setting means that the Jamf Pro agent installed on a Mac isn’t verifying certificate trust for the SSL certificate that Tomcat is using at enrollment, but does verify that the SSL certificate is trusted for all subsequent communication.

Note: The Always except during enrollment setting was meant to ensure that Jamf Pro could install a root certificate for a self-signed certificate and establish certificate trust that way.

Screen Shot 2022 09 09 at 10 16 14 AM

 

If your Jamf Pro service is using a publicly trusted SSL certificate, the fix is to set the SSL Certificate Verification setting to the following:

  • Always

Screen Shot 2022 09 09 at 10 16 24 AM

Selecting that setting and clicking the Save button will result in the following warning being displayed. If you’re certain you have a publicly trusted certificate, click OK. Otherwise, click the Cancel button to back the change out.

Screen Shot 2022 09 12 at 10 40 55 AM

As long as you have a publicly-trusted SSL certificate for Tomcat, changing the SSL Certificate Verification setting to Always should have no impact. 

If you’re hosted in Jamf Cloud, you should already be using a publicly trusted SSL certificate. If you’re hosting Jamf Pro yourself, I recommend verifying that you’re using a publicly trusted certificate before making that change.

If you are hosting Jamf Pro yourself and don’t have a publicly trusted SSL certificate for Tomcat, I strongly recommend getting one as soon as possible. As Jamf’s release notes mention, the option to not use a trusted certificate will be removed from a future version of Jamf Pro.

Microsoft Defender and tamper protection

One of the features of Microsoft Defender for macOS is tamper protection. This option is designed to prevent Defender or its settings from being removed or changed.

As of posting date, Defender’s tamper protection has three associated topics:

  • Disabled: Tamper protection is completely off.
  • Audit: Tampering operations are logged, but not blocked.
  • Blocked: Tamper protection is on, tampering operations are blocked.

Microsoft has documentation regarding Defender’s tamper protection for macOS, available via the link below:

https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/tamperprotection-macos

For more details, please see below the jump.

You can manage tamper protection via running commands via the command line, or via management profiles. The commands shown below allow tamper protection to be disabled completely, set to audit mode, or set to full tamper protection where Defender or its settings can’t be removed or changed.

To disable tamper protection, run the following command with root privileges:


/usr/local/bin/mdatp config tamper-protection enforcement-level –value disabled

view raw

gistfile1.txt

hosted with ❤ by GitHub

To set tamper protection to audit mode, run the following command with root privileges:


/usr/local/bin/mdatp config tamper-protection enforcement-level –value audit

view raw

gistfile1.txt

hosted with ❤ by GitHub

To set tamper protection to full tamper protection mode, run the following command with root privileges:


/usr/local/bin/mdatp config tamper-protection enforcement-level –value block

view raw

gistfile1.txt

hosted with ❤ by GitHub

If management via profiles is desired, the example profiles below will set Defender to audit mode or to full tamper protection.

Audit mode:


<?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"&gt;
<plist version="1">
<dict>
<key>PayloadUUID</key>
<string>05F0ADFE-8BE0-4C43-BACB-BD0DE6272306</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Enabling Tamper Protection for Microsoft Defender</string>
<key>PayloadDescription</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>PayloadRemovalDisallowed</key>
<true />
<key>PayloadScope</key>
<string>System</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadUUID</key>
<string>977D6F1E-E6C1-4BD2-96C5-D4FFCEAE92BB</string>
<key>PayloadType</key>
<string>com.microsoft.wdav</string>
<key>PayloadOrganization</key>
<string>Microsoft</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadDescription</key>
<string />
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>tamperProtection</key>
<dict>
<key>enforcementLevel</key>
<string>audit</string>
</dict>
</dict>
</array>
</dict>
</plist>

view raw

gistfile1.txt

hosted with ❤ by GitHub

Full tamper protection:


<?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"&gt;
<plist version="1">
<dict>
<key>PayloadUUID</key>
<string>E21D6405-FA0F-46D2-84DE-33775762DD7E</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Enabling Tamper Protection for Microsoft Defender</string>
<key>PayloadDescription</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>PayloadRemovalDisallowed</key>
<true />
<key>PayloadScope</key>
<string>System</string>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadUUID</key>
<string>C3934617-C698-4A88-819D-47377A35D4F3</string>
<key>PayloadType</key>
<string>com.microsoft.wdav</string>
<key>PayloadOrganization</key>
<string>Microsoft</string>
<key>PayloadIdentifier</key>
<string>com.microsoft.wdav</string>
<key>PayloadDisplayName</key>
<string>Microsoft Defender tamper protection configuration settings</string>
<key>PayloadDescription</key>
<string />
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true />
<key>tamperProtection</key>
<dict>
<key>enforcementLevel</key>
<string>block</string>
</dict>
</dict>
</array>
</dict>
</plist>

view raw

gistfile1.txt

hosted with ❤ by GitHub

To check the current Defender configuration, the following command can be run:


/usr/local/bin/mdatp health

view raw

gistfile1.txt

hosted with ❤ by GitHub

That should return output which looks similar to what’s shown below:


healthy : true
health_issues : []
licensed : true
engine_version : "1.1.18900.4"
app_version : "101.78.13"
org_id : "…"
log_level : "info"
machine_guid : "…"
release_ring : "Production"
product_expiration : Feb 05, 2023 at 06:24:12 AM
cloud_enabled : true
cloud_automatic_sample_submission_consent : "safe"
cloud_diagnostic_enabled : false
passive_mode_enabled : true [managed]
real_time_protection_enabled : false [managed]
real_time_protection_available : true
real_time_protection_subsystem : "endpoint_security_extension"
network_events_subsystem : "network_filter_extension"
device_control_enforcement_level : "audit"
tamper_protection : "audit"
automatic_definition_update_enabled : true
definitions_updated : Sept 08, 2022 at 01:57:03 PM
definitions_updated_minutes_ago : 27712166
definitions_version : "1.355.2589.0"
definitions_status : "update_failed"
edr_early_preview_enabled : "enabled"
edr_device_tags : []
edr_group_ids : ""
edr_configuration_version : "20.199999.main.2022.09.08.01-10dcd7fedfed0c7a1c3bbf153ba3c9b0d0f36239"
edr_machine_id : "…"
conflicting_applications : []
network_protection_status : "stopped"
network_protection_enforcement_level : "disabled"
data_loss_prevention_status : "disabled"

view raw

gistfile1.txt

hosted with ❤ by GitHub

To check specifically for the tamper protection configuration, the following command can be run to check its status:


/usr/local/bin/mdatp health –field tamper_protection

view raw

gistfile1.txt

hosted with ❤ by GitHub

You should see the following output depending on the configuration:

Tamper protection disabled:


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"disabled"
username@computername ~ %

view raw

gistfile1.txt

hosted with ❤ by GitHub

Tamper protection set to audit mode:


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"audit"
username@computername ~ %

view raw

gistfile1.txt

hosted with ❤ by GitHub

Tamper protection set to full tamper protection:


username@computername ~ % /usr/local/bin/mdatp health –field tamper_protection
"block"
username@computername ~ %

view raw

gistfile1.txt

hosted with ❤ by GitHub

For folks with uninstall scripts for Microsoft Defender, they won’t be able to successfully uninstall Defender or its settings with tamper protection enabled. One way to address this is to add a section at the beginning of the uninstall script which can detect if full tamper protection is enabled and stop the script if it is. An example of this is shown below:


#!/bin/bash
ERROR=0
# Check to see if Microsoft Defender's tamper protection is enabled.
#
# If tamper protection is turned on, a message will be displayed followed
# by the script exiting before proceeding to the uninstall functions of
# the script.
# Verify that the following tool is installed and executable:
#
# /usr/local/bin/mdatp
if [[ -x "/usr/local/bin/mdatp" ]]; then
# If the mdatp is installed, Defender's Tamper protection's
# status is checked by running the following command:
#
# /usr/local/bin/mdatp" health –field tamper_protection
#
# The output of this command will then be checked against the value stored
# in the tamper_protection_enabled_keyword variable.
#
# There are three possible keywords that can be returned by this command:
#
# disabled – tamper protection is completely off.
# audit – tampering operations are logged, but not blocked.
# block – tamper protection is on, tampering operations are blocked.
#
# The tamper_protection_enabled_keyword variable will store the keyword
# currently being used by Defender, in case Microsoft chooses to change
# the keywords in future versions of Defender.
tamper_protection_enabled="$("/usr/local/bin/mdatp" health –field tamper_protection | awk '{print $1}' | tr -d '"')"
tamper_protection_enabled_keyword="block"
if [[ "$tamper_protection_enabled" == "$tamper_protection_enabled_keyword" ]]; then
/usr/bin/osascript -e 'display dialog "Tamper protection for Microsoft Defender is enabled." & "\n" & "\nDefender cannot be uninstalled while tamper protection is turned on."& "\n" & "\nFor more information, please contact the helpdesk."buttons {"Understood"} default button 1 with icon Caution'
exit "$ERROR"
fi
fi

view raw

gistfile1.txt

hosted with ❤ by GitHub

If full tamper protection is enabled, this example script will take the following actions:

1. Display the following message using osascript.

Screen Shot 2022 09 09 at 9 27 38 AM

2. Exit after displaying the message.

Les vraies preuves d’amour

Dans les films hollywoodiens, les humains prouvent leur amour par de grandes déclarations enflammées. Dans les contes de fées, on voit toujours le moment où la princesse succombe au prince charmant, mais jamais comment ils font pour s’aimer une fois l’idylle passée. Il est facile de faire semblant d’aimer avec des mots ou de la séduction. Il est infiniment plus difficile (et moins démonstratif) de prouver son amour par nos actions.

L'ancien alcoolique qui se promène avec une bouteille de vin

Une ancienne personne alcoolique qui est réellement devenue sobre ne sortira jamais dans la rue avec une bouteille de vin, même si c’est de la piquette et que son bouchon est fermé. Elle sait que personne ne la croira lorsqu’elle dira qu’elle n’a pas rechuté. Et si elle ne se ment pas à elle-même, elle sait sa guérison fragile et la rechute tellement tentante et facile. Un ancien alcoolique qui se promène avec une bouteille de piquette est une personne qui est prête à faire douter les personnes qui ont souffert de son vice.

Privileges.app and time-limited admin

Privileges is an open source tool from SAP which helps folks manage admin rights for their account. As part of its feature set, it includes an option for time-limited admin using a specific function called Toggle privileges.

Privileges dock toggleon

Privileges dock toggleon20

However, Toggle privileges’s time-limited admin feature for Privileges is its most misunderstood feature. The reason is that while the ability to set a time limit is only available if you’re using the Toggle privileges function, many users assume that this time-limited admin is available universally to all the functions used to get admin rights using the Privileges app.

It is not. Time limited admin is only available using the Toggle privileges function. If you’re not using the Toggle privileges function, there is no time limitation and you cannot set one from within the Privileges app.

This information is available in the Privileges FAQ:

Screen Shot 2022 07 22 at 10 05 50 AM

What does this mean?

  1. The only way time-limited admin is currently working on Privileges is by using the Toggle privileges function.
  2. If you are clicking on the icon in the dock and not selecting the Toggle privileges function, there’s no time limit.
  3. If you’re using the PrivilegesCLI command line tool, there is no time limit.

How long do you have admin if you’re not using the Toggle privileges function? Admin rights are granted until some process (like running Privileges again) takes them away. There’s no time limit.

All of the Privileges management options available for time-limited admin at this time apply only to the Toggle privileges function. If you’re using any of the management settings options listed below, they apply only and exclusively to the Toggle privileges function:

  • DockToggleTimeout
  • DockToggleMaxTimeout

They will not manage time-limited admin for any of Privileges’ functions outside of using the Toggle privileges function.

What if you want time-limited admin outside of using the Toggle privileges function? You will need to use a separate mechanism. In my case, I usually point folks towards using PrivilegesDemoter:

https://github.com/sgmills/PrivilegesDemoter

This tool uses a separate mechanism for figuring out the timing and then uses the PrivilegesCLI command line tool to take away admin when the time limit set for PrivilegesDemoter expires.

J'ai un compte Facebook bordel !

Je sais, ça n’a pas de sens. Je quitte Twitter alors qu’Elon rate sa tentative de rachat, et en parallèle je crée un compte sur le réseau social que j’abhorre. J’ai pesé le pour et le contre. Au final, j’ai fini par m’y résoudre : j’ai donné mes quelques données personnelles pour pouvoir être connecté à mes centres d’intérêt, comme les groupes de musique. C’est sale, mais tant pis. Et pour Twitter, je suis partagé : d’un côté Musk a raté son rachat, mais d’un autre côté la plateforme ne me manque pas vraiment.

Specifying shell commands to run when opening new Terminal windows from macOS’s Terminal settings

As a follow-up to a previous post, as part of that post I had been running certain shell commands by adding them to a .zshrc file:

With some additional research, I learned that I could also run these commands using the Run command function which is available in your Terminal settings under the Shell tab.

Screen Shot 2022 07 15 at 11 17 29 AM

To replicate what I wanted, I had to enable the Run command option in the Shell tab, then also set Run inside shell. Once those were enabled, I added the following shell commands:

export PS1="\$ " &amp;&amp; unset zle_bracketed_paste &amp;&amp; clear
  • export PS1=”\$ “: Sets the prompt to only display “$” (no quotes) using the PS1 environmental variable.
  • unset zle_bracketed_paste: Disable the zsh shell’s bracketed paste feature.
  • clear: Removes all contents (including running the commands listed above) from the Terminal window.

The reason why this is nice is that I can now add running these commands to a macOS configuration profile using the CommandString key:


<key>CommandString</key>
<string>export PS1="\$ " &amp;&amp; unset zle_bracketed_paste &amp;&amp; clear</string>

view raw

gistfile1.txt

hosted with ❤ by GitHub

To see this used in context in a macOS configuration profile, please see below the jump.

The following profile sets the following settings:

  • Font: Monaco 18 point size

Additional settings:

  • Terminal prompt should not show the hostname or the logged-in user.
  • Zsh’s bracketed paste feature is disabled


<?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"&gt;
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadContent</key>
<dict>
<key>com.apple.Terminal</key>
<dict>
<key>Forced</key>
<array>
<dict>
<key>mcx_preference_settings</key>
<dict>
<key>Default Window Settings</key>
<string>Documentation</string>
<key>Startup Window Settings</key>
<string>Documentation</string>
<key>Window Settings</key>
<dict>
<key>Documentation</key>
<dict>
<key>CommandString</key>
<string>export PS1="\$ " &amp;&amp; unset zle_bracketed_paste &amp;&amp; clear</string>
<key>Font</key>
<data>YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0
b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRy
b290gAGkCwwVFlUkbnVsbNQNDg8QERITFFZOU1NpemVYTlNmRmxh
Z3NWTlNOYW1lViRjbGFzcyNAMgAAAAAAABAQgAKAA1ZNb25hY2/S
FxgZGlokY2xhc3NuYW1lWCRjbGFzc2VzVk5TRm9udKIZG1hOU09i
amVjdAgRGiQpMjdJTFFTWF5nbnd+hY6QkpSboKu0u74AAAAAAAAB
AQAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAxw==</data>
<key>FontAntialias</key>
<true/>
<key>FontWidthSpacing</key>
<real>1.004032258064516</real>
<key>Linewrap</key>
<true/>
<key>ProfileCurrentVersion</key>
<real>2.0699999999999998</real>
<key>name</key>
<string>Documentation</string>
<key>type</key>
<string>Window Settings</string>
</dict>
</dict>
</dict>
</dict>
</array>
</dict>
</dict>
<key>PayloadEnabled</key>
<true/>
<key>PayloadIdentifier</key>
<string>E7623CA6-76D7-4A3A-B35D-B1007986282A.terminal.profile.settings.40F1AB26-EAE7-4589-8101-72A4AC0C2015</string>
<key>PayloadType</key>
<string>com.apple.ManagedClient.preferences</string>
<key>PayloadUUID</key>
<string>40F1AB26-EAE7-4589-8101-72A4AC0C2015</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDescription</key>
<string>This configuration profile installs the Documentation Terminal profile and sets it as the default Terminal profile.</string>
<key>PayloadDisplayName</key>
<string>Sets Documentation Terminal profile</string>
<key>PayloadIdentifier</key>
<string>Documentation.41423E4C-72C8-48D1-BE24-734B62D7F77F.terminal.profile.settings.</string>
<key>PayloadOrganization</key>
<string>Company Name</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadScope</key>
<string>System</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>E7623CA6-76D7-4A3A-B35D-B1007986282A</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>