diff --git a/.github/scripts/dogfood-policy-updater-latest-macos.sh b/.github/scripts/dogfood-policy-updater-latest-macos.sh index 6d8186ba2a..d1998f9e0c 100644 --- a/.github/scripts/dogfood-policy-updater-latest-macos.sh +++ b/.github/scripts/dogfood-policy-updater-latest-macos.sh @@ -16,24 +16,25 @@ if [ -z "$DOGFOOD_AUTOMATION_TOKEN" ] || [ -z "$DOGFOOD_AUTOMATION_USER_NAME" ] fi # Function to calculate 4 Sundays from today -calculate_deadline() { - # Get current date - current_date=$(date +%Y-%m-%d) - - # Calculate days until next Sunday (0 = Sunday, 1 = Monday, ..., 6 = Saturday) - current_day=$(date +%u) # 1-7 (Monday=1, Sunday=7) - days_to_next_sunday=$((7 - current_day)) - if [ $days_to_next_sunday -eq 0 ]; then - days_to_next_sunday=7 - fi - - # Calculate 4 Sundays from today - days_to_deadline=$((days_to_next_sunday + 21)) # 3 more weeks (21 days) - - # Calculate the deadline date - deadline_date=$(date -d "$current_date + $days_to_deadline days" +%Y-%m-%d) - echo "$deadline_date" -} +# COMMENTED OUT: Deadline calculation logic temporarily disabled +# calculate_deadline() { +# # Get current date +# current_date=$(date +%Y-%m-%d) +# +# # Calculate days until next Sunday (0 = Sunday, 1 = Monday, ..., 6 = Saturday) +# current_day=$(date +%u) # 1-7 (Monday=1, Sunday=7) +# days_to_next_sunday=$((7 - current_day)) +# if [ $days_to_next_sunday -eq 0 ]; then +# days_to_next_sunday=7 +# fi +# +# # Calculate 4 Sundays from today +# days_to_deadline=$((days_to_next_sunday + 21)) # 3 more weeks (21 days) +# +# # Calculate the deadline date +# deadline_date=$(date -d "$current_date + $days_to_deadline days" +%Y-%m-%d) +# echo "$deadline_date" +# } # Function to fetch file content from GitHub fetch_file_content() { @@ -58,26 +59,28 @@ extract_minimum_version() { } # Function to extract current deadline from team file content -extract_deadline() { - local content="$1" - local deadline=$(echo "$content" | grep -A 5 "macos_updates:" | grep "deadline:" | sed 's/.*deadline: *"\([^"]*\)".*/\1/') - echo "$deadline" -} +# COMMENTED OUT: Deadline extraction logic temporarily disabled +# extract_deadline() { +# local content="$1" +# local deadline=$(echo "$content" | grep -A 5 "macos_updates:" | grep "deadline:" | sed 's/.*deadline: *"\([^"]*\)".*/\1/') +# echo "$deadline" +# } # Function to update team file content with new version and deadline -update_team_file_content() { - local content="$1" - local new_version="$2" - local new_deadline="$3" - - # Update minimum_version - content=$(echo "$content" | sed "s/minimum_version: \"[^\"]*\"/minimum_version: \"$new_version\"/") - - # Update deadline - content=$(echo "$content" | sed "s/deadline: \"[^\"]*\"/deadline: \"$new_deadline\"/") - - echo "$content" -} +# COMMENTED OUT: Team file update logic temporarily disabled +# update_team_file_content() { +# local content="$1" +# local new_version="$2" +# local new_deadline="$3" +# +# # Update minimum_version +# content=$(echo "$content" | sed "s/minimum_version: \"[^\"]*\"/minimum_version: \"$new_version\"/") +# +# # Update deadline +# content=$(echo "$content" | sed "s/deadline: \"[^\"]*\"/deadline: \"$new_deadline\"/") +# +# echo "$content" +# } # Fetch the latest macOS version echo "Fetching latest macOS version..." @@ -93,7 +96,8 @@ echo "Latest macOS version: $latest_macos_version" # Initialize update flags policy_update_needed=false -team_updates_needed=false +# COMMENTED OUT: Team updates flag temporarily disabled +# team_updates_needed=false updates_needed=false # Check policy file @@ -127,52 +131,53 @@ if [ "$policy_version_number" != "$latest_macos_version" ]; then updates_needed=true fi +# COMMENTED OUT: Team files check logic temporarily disabled # Check team files -echo "Checking team files..." -workstations_content=$(fetch_file_content "$WORKSTATIONS_FILE") -if [ $? -ne 0 ]; then - echo "Warning: Could not fetch workstations file, skipping team updates." -else - workstations_canary_content=$(fetch_file_content "$WORKSTATIONS_CANARY_FILE") - if [ $? -ne 0 ]; then - echo "Warning: Could not fetch workstations-canary file, skipping team updates." - else - # Extract current versions and deadlines - current_workstations_version=$(extract_minimum_version "$workstations_content") - current_workstations_deadline=$(extract_deadline "$workstations_content") - current_workstations_canary_version=$(extract_minimum_version "$workstations_canary_content") - current_workstations_canary_deadline=$(extract_deadline "$workstations_canary_content") - - echo "Current Workstations minimum_version: $current_workstations_version" - echo "Current Workstations deadline: $current_workstations_deadline" - echo "Current Workstations (canary) minimum_version: $current_workstations_canary_version" - echo "Current Workstations (canary) deadline: $current_workstations_canary_deadline" - - # Calculate new deadline - new_deadline=$(calculate_deadline) - echo "New deadline (4 Sundays from today): $new_deadline" - - # Check if team updates are needed - # Only update deadline if there's a new macOS version - if [ "$current_workstations_version" != "$latest_macos_version" ]; then - team_updates_needed=true - updates_needed=true - elif [ "$current_workstations_deadline" != "$new_deadline" ] && [ "$policy_update_needed" = true ]; then - # Only update deadline if policy was updated (meaning there's a new version) - team_updates_needed=true - updates_needed=true - fi - - if [ "$current_workstations_canary_version" != "$latest_macos_version" ]; then - team_updates_needed=true - updates_needed=true - elif [ "$current_workstations_canary_deadline" != "$new_deadline" ] && [ "$policy_update_needed" = true ]; then - # Only update deadline if policy was updated (meaning there's a new version) - team_updates_needed=true - updates_needed=true - fi - fi -fi +# echo "Checking team files..." +# workstations_content=$(fetch_file_content "$WORKSTATIONS_FILE") +# if [ $? -ne 0 ]; then +# echo "Warning: Could not fetch workstations file, skipping team updates." +# else +# workstations_canary_content=$(fetch_file_content "$WORKSTATIONS_CANARY_FILE") +# if [ $? -ne 0 ]; then +# echo "Warning: Could not fetch workstations-canary file, skipping team updates." +# else +# # Extract current versions and deadlines +# current_workstations_version=$(extract_minimum_version "$workstations_content") +# current_workstations_deadline=$(extract_deadline "$workstations_content") +# current_workstations_canary_version=$(extract_minimum_version "$workstations_canary_content") +# current_workstations_canary_deadline=$(extract_deadline "$workstations_canary_content") +# +# echo "Current Workstations minimum_version: $current_workstations_version" +# echo "Current Workstations deadline: $current_workstations_deadline" +# echo "Current Workstations (canary) minimum_version: $current_workstations_canary_version" +# echo "Current Workstations (canary) deadline: $current_workstations_canary_deadline" +# +# # Calculate new deadline +# new_deadline=$(calculate_deadline) +# echo "New deadline (4 Sundays from today): $new_deadline" +# +# # Check if team updates are needed +# # Only update deadline if there's a new macOS version +# if [ "$current_workstations_version" != "$latest_macos_version" ]; then +# team_updates_needed=true +# updates_needed=true +# elif [ "$current_workstations_deadline" != "$new_deadline" ] && [ "$policy_update_needed" = true ]; then +# # Only update deadline if policy was updated (meaning there's a new version) +# team_updates_needed=true +# updates_needed=true +# fi +# +# if [ "$current_workstations_canary_version" != "$latest_macos_version" ]; then +# team_updates_needed=true +# updates_needed=true +# elif [ "$current_workstations_canary_deadline" != "$new_deadline" ] && [ "$policy_update_needed" = true ]; then +# # Only update deadline if policy was updated (meaning there's a new version) +# team_updates_needed=true +# updates_needed=true +# fi +# fi +# fi # Create updates if needed if [ "$updates_needed" = true ]; then @@ -205,17 +210,18 @@ if [ "$updates_needed" = true ]; then git add "$POLICY_FILE_PATH" fi + # COMMENTED OUT: Team files update logic temporarily disabled # Update team files if needed - if [ "$team_updates_needed" = true ]; then - echo "Updating team files..." - updated_workstations_content=$(update_team_file_content "$workstations_content" "$latest_macos_version" "$new_deadline") - updated_canary_content=$(update_team_file_content "$workstations_canary_content" "$latest_macos_version" "$new_deadline") - - echo "$updated_workstations_content" > "$WORKSTATIONS_FILE" - echo "$updated_canary_content" > "$WORKSTATIONS_CANARY_FILE" - - git add "$WORKSTATIONS_FILE" "$WORKSTATIONS_CANARY_FILE" - fi + # if [ "$team_updates_needed" = true ]; then + # echo "Updating team files..." + # updated_workstations_content=$(update_team_file_content "$workstations_content" "$latest_macos_version" "$new_deadline") + # updated_canary_content=$(update_team_file_content "$workstations_canary_content" "$latest_macos_version" "$new_deadline") + # + # echo "$updated_workstations_content" > "$WORKSTATIONS_FILE" + # echo "$updated_canary_content" > "$WORKSTATIONS_CANARY_FILE" + # + # git add "$WORKSTATIONS_FILE" "$WORKSTATIONS_CANARY_FILE" + # fi # Create commit message commit_message="Update macOS version to $latest_macos_version" @@ -224,12 +230,13 @@ if [ "$updates_needed" = true ]; then - Updated policy version from $policy_version_number to $latest_macos_version" fi - if [ "$team_updates_needed" = true ]; then - commit_message="$commit_message -- Updated team minimum_version from $current_workstations_version to $latest_macos_version -- Updated team deadline from $current_workstations_deadline to $new_deadline (4 Sundays from today) -- Applied to both workstations and workstations-canary teams" - fi + # COMMENTED OUT: Team updates commit message logic temporarily disabled + # if [ "$team_updates_needed" = true ]; then + # commit_message="$commit_message + # - Updated team minimum_version from $current_workstations_version to $latest_macos_version + # - Updated team deadline from $current_workstations_deadline to $new_deadline (4 Sundays from today) + # - Applied to both workstations and workstations-canary teams" + # fi git commit -m "$commit_message" git push origin "$NEW_BRANCH" diff --git a/it-and-security/default.yml b/it-and-security/default.yml index b4585150de..6038cc308d 100644 --- a/it-and-security/default.yml +++ b/it-and-security/default.yml @@ -107,3 +107,4 @@ labels: - path: ./lib/all/labels/macos-compatibility-extension-installed.yml - path: ./lib/all/labels/team-g-mdm.yml - path: ./lib/all/labels/conditional-access-test-group.yml + - path: ./lib/all/labels/nudge-test-devices.yml diff --git a/it-and-security/lib/all/labels/nudge-test-devices.yml b/it-and-security/lib/all/labels/nudge-test-devices.yml new file mode 100644 index 0000000000..db5aa62669 --- /dev/null +++ b/it-and-security/lib/all/labels/nudge-test-devices.yml @@ -0,0 +1,7 @@ +- name: Nudge test devices + description: Macs testing Nudge + label_membership_type: manual + platform: darwin + hosts: + - "allens-macbook-pro.local" + - "allens-mac-mini.local" diff --git a/it-and-security/lib/macos/configuration-profiles/nudge-configuration.mobileconfig b/it-and-security/lib/macos/configuration-profiles/nudge-configuration.mobileconfig new file mode 100644 index 0000000000..b69acc5c12 --- /dev/null +++ b/it-and-security/lib/macos/configuration-profiles/nudge-configuration.mobileconfig @@ -0,0 +1,179 @@ + + + + + PayloadContent + + + PayloadDisplayName + Nudge Preferences + PayloadIdentifier + com.fleetdm.nudge.preferences + PayloadType + com.github.macadmins.Nudge + PayloadUUID + 69B22694-8FF8-40A0-AEE6-D5385BBF765D + PayloadVersion + 1 + optionalFeatures + + acceptableApplicationBundleIDs + + us.zoom.xos + + acceptableCameraUsage + + acceptableScreenSharingUsage + + aggressiveUserExperience + + asynchronousSoftwareUpdate + + attemptToFetchMajorUpgrade + + disableNudgeForStandardInstalls + + disableSoftwareUpdateWorkflow + + enforceMinorUpdates + + honorFocusModes + + utilizeSOFAFeed + + + osVersionRequirements + + + aboutUpdateURL + https://support.apple.com/en-us/120283 + requiredInstallationDate + 2025-09-05T00:00:00 + requiredMinimumOSVersion + latest-minor + targetedOSVersionsRule + 15 + + + aboutUpdateURL + http://fleetdm.com + requiredMinimumOSVersion + latest-minor + targetedOSVersionsRule + 26 + + + userExperience + + allowGracePeriods + + allowLaterDeferralButton + + allowMovableWindow + + allowUserQuitDeferrals + + allowedDeferrals + 1000000 + approachingRefreshCycle + 86400 + approachingWindowTime + 120 + elapsedRefreshCycle + 7200 + gracePeriodInstallDelay + 336 + gracePeriodLaunchDelay + 168 + imminentRefreshCycle + 86400 + imminentWindowTime + 0 + initialRefreshCycle + 259200 + nudgeMajorUpgradeEventLaunchDelay + 0 + nudgeMinorUpdateEventLaunchDelay + 0 + randomDelay + + + userInterface + + fallbackLanguage + en + forceFallbackLanguage + + iconDarkPath + /var/fleet/nudge-assets/fleet-logo-dark.png + iconLightPath + /var/fleet/nudge-assets/fleet-logo-light.png + showActivelyExploitedCVEs + + showDaysRemainingToUpdate + + showDeferralCount + + showRequiredDate + + simpleMode + + updateElements + + + _language + en + actionButtonText + Update + customDeferralButtonText + Custom + customDeferralDropdownText + Defer + informationButtonText + More Info + mainContentHeader + Your device will restart during this update + mainContentNote + Important Notes + mainContentSubHeader + Updates can take around 30 minutes to complete + mainContentText + Your computer is behind on one or more critical updates and must be updated to the OS Version indicated on the left, by the indicated deadline. + +This window will appear periodically to remind you until your computer is fully updated, appearing more frequently as the deadline date gets closer. +If you fail to update by the deadline, access and functionality may be limited. Please update now to avoid any downtime. + +If you have any questions or would like more information, please reach out via #help-it-and-enablement. + mainHeader + Software updates required + oneDayDeferralButtonText + One Day + oneHourDeferralButtonText + One Hour + primaryQuitButtonText + Later + secondaryQuitButtonText + I Understand + subHeader + A message from IT & Enablement + + + + + + PayloadDisplayName + Nudge settings + PayloadIdentifier + com.fleetdm.nudge.managed + PayloadOrganization + Fleet + PayloadScope + System + PayloadType + Configuration + PayloadUUID + 4B4C950F-995A-4567-B0B2-9A34EB4C22AC + PayloadVersion + 1 + + diff --git a/it-and-security/lib/macos/policies/install-nudge.yml b/it-and-security/lib/macos/policies/install-nudge.yml new file mode 100644 index 0000000000..55fc12fc43 --- /dev/null +++ b/it-and-security/lib/macos/policies/install-nudge.yml @@ -0,0 +1,12 @@ +- name: macOS - Nudge installed and configured + query: SELECT 1 WHERE EXISTS (SELECT 1 FROM macos_profiles WHERE identifier = "com.fleetdm.nudge.preferences") AND EXISTS (SELECT 1 FROM apps WHERE name = "Nudge"); + critical: false + description: This policy ensures the Nudge is installed and configured. + resolution: "If you are failing this policy, click Refetch. If you are still failing after Refetch completes, drop a note in #help-it-and-enablement." + run_script: + path: ../scripts/install-nudge.sh + install_software: + package_path: ../software/nudge-assets.yml + platform: darwin + labels_include_any: + - "Nudge test devices" diff --git a/it-and-security/lib/macos/scripts/install-nudge.sh b/it-and-security/lib/macos/scripts/install-nudge.sh new file mode 100755 index 0000000000..4c88c2a8ee --- /dev/null +++ b/it-and-security/lib/macos/scripts/install-nudge.sh @@ -0,0 +1,164 @@ +#!/bin/bash +set -e + +REPO_OWNER="macadmins" +REPO_NAME="nudge" +DOWNLOAD_DIR="${DOWNLOAD_DIR:-./}" # Default to current directory, can be overridden +INSTALL_PACKAGE="${INSTALL_PACKAGE:-true}" # Default to install, can be overridden + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to check if running as root +check_root() { + if [[ $EUID -ne 0 ]]; then + print_error "This script must be run as root (use sudo)" + exit 1 + fi +} + +# Function to install the package silently +install_package() { + local filepath="$1" + + print_status "Installing Nudge package silently..." + + if ! installer -pkg "$filepath" -target /; then + print_error "Failed to install Nudge package" + exit 1 + fi + + print_success "Nudge package installed successfully" +} + +# Show usage information +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Downloads and installs the latest Nudge package from GitHub" + echo "" + echo "Options:" + echo " -d, --dir DIR Download directory (default: current directory)" + echo " -h, --help Show this help message" + echo " --download-only Download only, do not install" + echo "" + echo "Environment variables:" + echo " DOWNLOAD_DIR Override default download directory" + echo " INSTALL_PACKAGE Set to 'false' to download only" + echo "" + echo "Examples:" + echo " $0 # Download and install to system" + echo " $0 -d /tmp # Download to /tmp and install" + echo " $0 --download-only # Download only, do not install" + echo " INSTALL_PACKAGE=false $0 # Download only using env var" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -d|--dir) + DOWNLOAD_DIR="$2" + shift 2 + ;; + -h|--help) + show_usage + exit 0 + ;; + --download-only) + INSTALL_PACKAGE="false" + shift + ;; + *) + print_error "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +print_status "Starting Nudge download and installation script..." + +# Check if running as root (required for installation) +if [[ "$INSTALL_PACKAGE" == "true" ]]; then + check_root +fi + +# Check dependencies +if ! command -v curl &> /dev/null; then + print_error "curl is required but not installed" + exit 1 +fi + +if ! command -v jq &> /dev/null; then + print_error "jq is required but not installed" + exit 1 +fi + +# Get latest release information +print_status "Fetching latest release information..." +api_url="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" +release_info=$(curl -s "$api_url") + +# Extract version +print_status "Extracting version information..." +tag_name=$(echo "$release_info" | jq -r '.tag_name') + +if [ "$tag_name" = "null" ] || [ -z "$tag_name" ]; then + print_error "Could not extract tag name from release information" + exit 1 +fi + +# Remove 'v' prefix if present +version=$(echo "$tag_name" | sed 's/^v//') +print_status "Latest version: v${version}" + +# Construct download URL +download_url="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/v${version}/Nudge-${version}.pkg" +filename="Nudge-${version}.pkg" +filepath="${DOWNLOAD_DIR}/${filename}" + +print_status "Downloading Nudge v${version}..." +print_status "URL: $download_url" +print_status "Destination: $filepath" + +# Create download directory if it doesn't exist +mkdir -p "$DOWNLOAD_DIR" + +# Download with progress bar and follow redirects +if curl -L --progress-bar -o "$filepath" "$download_url"; then + print_success "Downloaded: $filepath" + + # Display file information + if [ -f "$filepath" ]; then + file_size=$(ls -lh "$filepath" | awk '{print $5}') + print_status "File size: $file_size" + fi +else + print_error "Failed to download $filename" + exit 1 +fi + +# Install the package if requested +if [[ "$INSTALL_PACKAGE" == "true" ]]; then + install_package "$filepath" + print_success "Nudge v${version} downloaded and installed successfully!" +else + print_success "Nudge v${version} downloaded successfully!" + print_status "Package location: $filepath" + print_status "Run 'sudo installer -pkg \"$filepath\" -target /' to install manually" +fi diff --git a/it-and-security/lib/macos/software/nudge-assets.yml b/it-and-security/lib/macos/software/nudge-assets.yml new file mode 100644 index 0000000000..d369e84fb4 --- /dev/null +++ b/it-and-security/lib/macos/software/nudge-assets.yml @@ -0,0 +1,5 @@ +hash_sha256: 4497673bda128fbb485f19dd96ba0b0b787d6cdbbbce569a084d326b63e3c866 +self_service: false +categories: +labels_include_any: + - "Nudge test devices" diff --git a/it-and-security/lib/macos/software/nudge.yml b/it-and-security/lib/macos/software/nudge.yml new file mode 100644 index 0000000000..7e05c1db8d --- /dev/null +++ b/it-and-security/lib/macos/software/nudge.yml @@ -0,0 +1,5 @@ +url: https://github.com/macadmins/nudge/releases/download/v2.0.12.81807/Nudge-2.0.12.81807.pkg +self_service: false +categories: +labels_include_any: + - "Nudge test devices" diff --git a/it-and-security/teams/workstations-canary.yml b/it-and-security/teams/workstations-canary.yml index d30cba0205..19cfef2b54 100644 --- a/it-and-security/teams/workstations-canary.yml +++ b/it-and-security/teams/workstations-canary.yml @@ -109,6 +109,9 @@ controls: - path: ../lib/macos/configuration-profiles/santa-rules.mobileconfig labels_include_any: - "Santa test devices" + - path: ../lib/macos/configuration-profiles/nudge-configuration.mobileconfig + labels_include_any: + - "Nudge test devices" macos_setup: bootstrap_package: "" enable_end_user_authentication: false @@ -146,6 +149,7 @@ controls: - path: ../lib/linux/scripts/install-fleet-desktop-required-extension.sh - path: ../lib/macos/scripts/install-santa-extension.sh - path: ../lib/macos/scripts/install-macos-compatibility-extension.sh + - path: ../lib/macos/scripts/install-nudge.sh policies: - path: ../lib/macos/policies/1password-emergency-kit-check.yml - path: ../lib/macos/policies/update-firefox.yml @@ -179,6 +183,8 @@ software: - path: ../lib/macos/software/zoom.yml # Zoom for macOS - path: ../lib/macos/software/fleet-keynote-theme.yml # Fleet Keynote theme for macOS - path: ../lib/macos/software/company-portal.yml # Company Portal for macOS + - path: ../lib/macos/software/nudge.yml # Nudge for macOS + - path: ../lib/macos/software/nudge-assets.yml # Nudge assets for macOS - path: ../lib/linux/software/zoom-deb.yml # Zoom for Ubuntu - path: ../lib/linux/software/zoom-rpm.yml # Zoom for RedHat - path: ../lib/linux/software/slack-deb.yml # Slack for Ubuntu