Added additional logic for Keynote theme installer (#30029)
- Added a new `Keynote installed` label so that the Keynote theme installer will only show up for devices with Keynote installed - Added a new `refetch_host.sh` to use to trigger an immediate refetch
This commit is contained in:
@@ -102,3 +102,4 @@ labels:
|
||||
- path: ./lib/all/labels/santa-test-devices.yml
|
||||
- path: ./lib/all/labels/x86-based-windows-hosts.yml
|
||||
- path: ./lib/all/labels/apple-silicon-macos-hosts.yml
|
||||
- path: ./lib/all/labels/keynote-installed.yml
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- name: Keynote installed
|
||||
description: macOS hosts with Keynote installed
|
||||
query: SELECT 1 FROM apps WHERE name = "Keynote.app";
|
||||
label_membership_type: dynamic
|
||||
platform: darwin
|
||||
+473
@@ -0,0 +1,473 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to refetch host details using Fleet's device authentication token
|
||||
# This script reads the device token from /opt/orbit/identifier and triggers a refetch
|
||||
|
||||
set -euo pipefail # Exit on error, undefined vars, and pipe failures
|
||||
|
||||
# Configuration
|
||||
IDENTIFIER_FILE="/opt/orbit/identifier"
|
||||
FLEET_URL="https://dogfood.fleetdm.com" # Set this environment variable or modify the script
|
||||
LOG_LEVEL="${LOG_LEVEL:-info}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
case "$level" in
|
||||
"error")
|
||||
echo -e "${timestamp} [${RED}ERROR${NC}] $message" >&2
|
||||
;;
|
||||
"warn")
|
||||
echo -e "${timestamp} [${YELLOW}WARN${NC}] $message" >&2
|
||||
;;
|
||||
"info")
|
||||
echo -e "${timestamp} [${GREEN}INFO${NC}] $message"
|
||||
;;
|
||||
"debug")
|
||||
if [[ "$LOG_LEVEL" == "debug" ]]; then
|
||||
echo -e "${timestamp} [DEBUG] $message"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to check if a command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to validate URL format
|
||||
validate_url() {
|
||||
local url="$1"
|
||||
if [[ ! "$url" =~ ^https?://[^[:space:]]+$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to read device token
|
||||
read_device_token() {
|
||||
local token_file="$1"
|
||||
|
||||
if [[ ! -f "$token_file" ]]; then
|
||||
log error "Device token file not found: $token_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -r "$token_file" ]]; then
|
||||
log error "Cannot read device token file: $token_file (permission denied)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local token
|
||||
token=$(cat "$token_file" 2>/dev/null)
|
||||
|
||||
if [[ -z "$token" ]]; then
|
||||
log error "Device token file is empty: $token_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Basic validation - Fleet device tokens should be non-empty strings
|
||||
if [[ ${#token} -lt 10 ]]; then
|
||||
log warn "Device token seems unusually short (${#token} characters)"
|
||||
fi
|
||||
|
||||
echo "$token"
|
||||
}
|
||||
|
||||
# Function to get host ID from device token
|
||||
get_host_id() {
|
||||
local fleet_url="$1"
|
||||
local device_token="$2"
|
||||
|
||||
log debug "Attempting to get host ID using device token..."
|
||||
|
||||
# Use the device endpoint to get basic host information
|
||||
local response
|
||||
local http_code
|
||||
|
||||
response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
|
||||
-H "Accept: application/json" \
|
||||
-H "User-Agent: fleet-refetch-script/1.0" \
|
||||
--max-time 30 \
|
||||
--retry 2 \
|
||||
--retry-delay 1 \
|
||||
"${fleet_url}/api/v1/fleet/device/${device_token}" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log error "Failed to connect to Fleet server at $fleet_url"
|
||||
return 1
|
||||
fi
|
||||
|
||||
http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g')
|
||||
|
||||
log debug "HTTP response code: $http_code"
|
||||
|
||||
if [[ "$http_code" -ne 200 ]]; then
|
||||
log error "Failed to authenticate with device token (HTTP $http_code)"
|
||||
if [[ "$http_code" -eq 401 ]]; then
|
||||
log error "Device token appears to be invalid or expired"
|
||||
elif [[ "$http_code" -eq 404 ]]; then
|
||||
log error "Device not found or Fleet server endpoint not available"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract host ID from JSON response
|
||||
local host_id
|
||||
if command_exists jq; then
|
||||
host_id=$(echo "$response_body" | jq -r '.host.id' 2>/dev/null)
|
||||
else
|
||||
# Fallback: basic grep/sed extraction (less reliable but doesn't require jq)
|
||||
host_id=$(echo "$response_body" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
fi
|
||||
|
||||
if [[ -z "$host_id" || "$host_id" == "null" ]]; then
|
||||
log error "Could not extract host ID from response"
|
||||
log debug "Response body: $response_body"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$host_id"
|
||||
}
|
||||
|
||||
# Function to trigger device-level refetch using device token
|
||||
trigger_device_refetch() {
|
||||
local fleet_url="$1"
|
||||
local device_token="$2"
|
||||
|
||||
log info "Triggering device refetch using device token..."
|
||||
|
||||
# Try device-specific refetch endpoint that may accept device tokens
|
||||
local response
|
||||
local http_code
|
||||
|
||||
# First attempt: Try device-specific refetch endpoint
|
||||
response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
|
||||
-X POST \
|
||||
-H "Accept: application/json" \
|
||||
-H "User-Agent: fleet-refetch-script/1.0" \
|
||||
--max-time 30 \
|
||||
--retry 2 \
|
||||
--retry-delay 1 \
|
||||
"${fleet_url}/api/v1/fleet/device/${device_token}/refetch" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log warn "Failed to connect to device-specific refetch endpoint, trying alternative..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g')
|
||||
|
||||
log debug "Device refetch HTTP response code: $http_code"
|
||||
|
||||
case "$http_code" in
|
||||
200|202)
|
||||
log info "Device refetch triggered successfully"
|
||||
return 0
|
||||
;;
|
||||
404)
|
||||
log warn "Device-specific refetch endpoint not available, trying alternative method..."
|
||||
return 1
|
||||
;;
|
||||
401|403)
|
||||
log error "Device token authentication failed for refetch"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
log warn "Device refetch returned HTTP $http_code, trying alternative method..."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to trigger refetch via orbit/fleetd ping mechanism
|
||||
trigger_orbit_ping() {
|
||||
local fleet_url="$1"
|
||||
local device_token="$2"
|
||||
|
||||
log info "Attempting to trigger refetch via orbit ping mechanism..."
|
||||
|
||||
local response
|
||||
local http_code
|
||||
|
||||
# Try the orbit device ping endpoint which may trigger a refetch
|
||||
response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
|
||||
-X POST \
|
||||
-H "Accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: fleet-refetch-script/1.0" \
|
||||
-d '{"node_key": "'$device_token'", "refetch_requested": true}' \
|
||||
--max-time 30 \
|
||||
--retry 2 \
|
||||
--retry-delay 1 \
|
||||
"${fleet_url}/api/fleet/orbit/ping" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log warn "Failed to connect to orbit ping endpoint"
|
||||
return 1
|
||||
fi
|
||||
|
||||
http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g')
|
||||
|
||||
log debug "Orbit ping HTTP response code: $http_code"
|
||||
|
||||
case "$http_code" in
|
||||
200)
|
||||
log info "Orbit ping successful - refetch may have been triggered"
|
||||
return 0
|
||||
;;
|
||||
404)
|
||||
log warn "Orbit ping endpoint not available"
|
||||
return 1
|
||||
;;
|
||||
401|403)
|
||||
log warn "Authentication failed for orbit ping"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
log warn "Orbit ping returned HTTP $http_code"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to simulate osquery check-in to trigger refetch
|
||||
trigger_osquery_checkin() {
|
||||
local fleet_url="$1"
|
||||
local device_token="$2"
|
||||
|
||||
log info "Attempting to trigger refetch via osquery distributed read..."
|
||||
|
||||
local response
|
||||
local http_code
|
||||
|
||||
# Simulate an osquery distributed read which should trigger refetch if requested
|
||||
response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
|
||||
-X POST \
|
||||
-H "Accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: fleet-refetch-script/1.0" \
|
||||
-d '{"node_key": "'$device_token'"}' \
|
||||
--max-time 30 \
|
||||
--retry 2 \
|
||||
--retry-delay 1 \
|
||||
"${fleet_url}/api/v1/osquery/distributed/read" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log warn "Failed to connect to osquery distributed endpoint"
|
||||
return 1
|
||||
fi
|
||||
|
||||
http_code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
|
||||
response_body=$(echo "$response" | sed -e 's/HTTPSTATUS:.*//g')
|
||||
|
||||
log debug "Osquery distributed read HTTP response code: $http_code"
|
||||
|
||||
case "$http_code" in
|
||||
200)
|
||||
# Check if refetch queries were returned
|
||||
if echo "$response_body" | grep -q "SELECT\|osquery_info\|system_info"; then
|
||||
log info "Osquery check-in successful - refetch queries may have been delivered"
|
||||
return 0
|
||||
else
|
||||
log info "Osquery check-in successful but no refetch queries detected"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
404)
|
||||
log warn "Osquery distributed endpoint not available"
|
||||
return 1
|
||||
;;
|
||||
401|403)
|
||||
log warn "Authentication failed for osquery distributed read"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
log warn "Osquery distributed read returned HTTP $http_code"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Main refetch function that tries multiple approaches
|
||||
trigger_refetch() {
|
||||
local fleet_url="$1"
|
||||
local device_token="$2"
|
||||
local host_id="$3"
|
||||
|
||||
log info "Attempting to trigger host refetch using device token..."
|
||||
|
||||
# Try multiple approaches in order of preference
|
||||
|
||||
# Method 1: Device-specific refetch endpoint
|
||||
if trigger_device_refetch "$fleet_url" "$device_token"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Method 2: Orbit ping mechanism
|
||||
if trigger_orbit_ping "$fleet_url" "$device_token"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Method 3: Osquery distributed read (may trigger refetch)
|
||||
if trigger_osquery_checkin "$fleet_url" "$device_token"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# If all methods failed, provide guidance
|
||||
log error "All refetch methods failed with the device token"
|
||||
log error ""
|
||||
log error "POSSIBLE SOLUTIONS:"
|
||||
log error "1. The device might not have Fleet Desktop installed"
|
||||
log error "2. Try restarting the fleetd/orbit service on this host to trigger a natural check-in"
|
||||
log error "3. Use an API token with admin/maintainer privileges instead:"
|
||||
log error " curl -X POST -H 'Authorization: Bearer YOUR_API_TOKEN' \\"
|
||||
log error " '${fleet_url}/api/v1/fleet/hosts/${host_id}/refetch'"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to display usage
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Refetch host details using Fleet's device authentication token.
|
||||
|
||||
OPTIONS:
|
||||
-u, --url URL Fleet server URL (can also be set via FLEET_URL env var)
|
||||
-f, --file FILE Path to device token file (default: /opt/orbit/identifier)
|
||||
-v, --verbose Enable debug logging
|
||||
-h, --help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
# Basic usage with Fleet URL as environment variable
|
||||
export FLEET_URL="https://fleet.example.com"
|
||||
$0
|
||||
|
||||
# Specify Fleet URL directly
|
||||
$0 --url "https://fleet.example.com"
|
||||
|
||||
# Use custom token file location
|
||||
$0 --url "https://fleet.example.com" --file "/custom/path/to/token"
|
||||
|
||||
# Enable verbose logging
|
||||
$0 --url "https://fleet.example.com" --verbose
|
||||
|
||||
NOTES:
|
||||
- The device must have Fleet Desktop installed
|
||||
- The device token is read from $IDENTIFIER_FILE by default
|
||||
- The Fleet server URL must include the protocol (http:// or https://)
|
||||
- This script requires curl to be installed
|
||||
- Optional: jq for better JSON parsing (will fall back to basic parsing if not available)
|
||||
- The refetch operation may require elevated privileges depending on Fleet configuration
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local fleet_url="$FLEET_URL"
|
||||
local token_file="$IDENTIFIER_FILE"
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-u|--url)
|
||||
fleet_url="$2"
|
||||
shift 2
|
||||
;;
|
||||
-f|--file)
|
||||
token_file="$2"
|
||||
shift 2
|
||||
;;
|
||||
-v|--verbose)
|
||||
LOG_LEVEL="debug"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log error "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validation
|
||||
if [[ -z "$fleet_url" ]]; then
|
||||
log error "Fleet server URL is required"
|
||||
log error "Set FLEET_URL environment variable or use --url option"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! validate_url "$fleet_url"; then
|
||||
log error "Invalid Fleet server URL: $fleet_url"
|
||||
log error "URL must start with http:// or https://"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove trailing slash from URL
|
||||
fleet_url="${fleet_url%/}"
|
||||
|
||||
# Check dependencies
|
||||
if ! command_exists curl; then
|
||||
log error "curl is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command_exists jq; then
|
||||
log warn "jq not found - will use basic JSON parsing (less reliable)"
|
||||
fi
|
||||
|
||||
log info "Starting Fleet host refetch process..."
|
||||
log debug "Fleet URL: $fleet_url"
|
||||
log debug "Token file: $token_file"
|
||||
|
||||
# Read device token
|
||||
log info "Reading device authentication token..."
|
||||
local device_token
|
||||
if ! device_token=$(read_device_token "$token_file"); then
|
||||
exit 1
|
||||
fi
|
||||
log debug "Device token length: ${#device_token} characters"
|
||||
|
||||
# Get host ID
|
||||
log info "Authenticating with Fleet server..."
|
||||
local host_id
|
||||
if ! host_id=$(get_host_id "$fleet_url" "$device_token"); then
|
||||
exit 1
|
||||
fi
|
||||
log info "Successfully authenticated - Host ID: $host_id"
|
||||
|
||||
# Trigger refetch
|
||||
if trigger_refetch "$fleet_url" "$device_token" "$host_id"; then
|
||||
log info "Host refetch completed successfully"
|
||||
log info "Note: It may take a few moments for the updated data to be available"
|
||||
exit 0
|
||||
else
|
||||
log error "Host refetch failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main function with all arguments
|
||||
main "$@"
|
||||
@@ -1,3 +1,5 @@
|
||||
hash_sha256: d720f17d6646bfc399b6e818eb04a3d097975e4996dc701706e46e6d16be0d45
|
||||
self_service: true
|
||||
categories:
|
||||
labels_include_any:
|
||||
- "Keynote installed"
|
||||
|
||||
Reference in New Issue
Block a user