mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
## Summary Adds a new pre-baked script to the [fleetdm.com/scripts](https://fleetdm.com/scripts) library: **Disable Wi-Fi auto-join** for macOS. ## Changes - Added a new entry to `docs/scripts.yml` in the macOS section - The script disables auto-join for a specified Wi-Fi SSID using `networksetup` and `PlistBuddy`/`defaults write` - Based on [Alan Siu's approach](https://www.alansiu.net/2026/01/22/scripting-disabling-auto-join-for-wi-fi-networks/) ## How to test 1. Build the website static content and verify the new script appears on the /scripts page 2. Verify the script slug doesn't collide with existing scripts --- Built for [mikermcneil](https://fleetdm.slack.com/archives/D0AFASLRHNU/p1770980976219219?thread_ts=1770980373.078179&cid=D0AFASLRHNU) by [Kilo for Slack](https://kilo.ai/features/slack-integration) --------- Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> Co-authored-by: Brock Walters <153771548+nonpunctual@users.noreply.github.com>
1255 lines
44 KiB
YAML
1255 lines
44 KiB
YAML
#
|
||
# ╔╦╗╔═╗╔═╗╔═╗╔═╗
|
||
# ║║║╠═╣║ ║ ║╚═╗
|
||
# ╩ ╩╩ ╩╚═╝╚═╝╚═╝
|
||
- name: Collect fleetd logs
|
||
platform: macos
|
||
description: Copies logs from the fleetd agent to a shared folder.
|
||
script: |
|
||
cp /var/log/orbit/orbit.stderr.log ~/Library/Logs/Fleet/fleet-desktop.log /Users/Shared
|
||
|
||
echo "Successfully copied fleetd logs to the /Users/Shared folder."
|
||
|
||
echo "To retrieve logs, ask the end user to open Finder and in the menu bar select Go > Go to Folder."
|
||
|
||
echo "Then, ask the end user to type in /Users/Shared, press Return, and locate orbit.stderr.log (Orbit logs) and fleet-desktop.log (Fleet Desktop logs) files."
|
||
|
||
- name: Create conditional access allow file
|
||
platform: macos
|
||
description: Create an allow file used for Microsoft Entra conditional access.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
# Script to check for entra-conditional-access-allow file and create it if needed
|
||
# Target location: /var/fleet/entra-conditional-access-allow
|
||
|
||
FILE_PATH="/var/fleet/entra-conditional-access-allow"
|
||
DIR_PATH="/var/fleet"
|
||
|
||
# Check if the directory exists, create if it doesn't
|
||
if [ ! -d "$DIR_PATH" ]; then
|
||
echo "Directory $DIR_PATH does not exist. Creating it..."
|
||
sudo mkdir -p "$DIR_PATH"
|
||
fi
|
||
|
||
# Check if the file exists
|
||
if [ -f "$FILE_PATH" ]; then
|
||
echo "File $FILE_PATH already exists."
|
||
|
||
# Check current permissions
|
||
CURRENT_PERMS=$(stat -f "%Lp" "$FILE_PATH" 2>/dev/null)
|
||
if [ "$CURRENT_PERMS" != "644" ]; then
|
||
echo "Current permissions: $CURRENT_PERMS. Updating to 644..."
|
||
sudo chmod 644 "$FILE_PATH"
|
||
echo "Permissions updated to 644."
|
||
else
|
||
echo "Permissions are already set to 644."
|
||
fi
|
||
else
|
||
echo "File $FILE_PATH does not exist. Creating it..."
|
||
|
||
# Create the file using touch
|
||
sudo touch "$FILE_PATH"
|
||
|
||
# Set permissions to 644
|
||
sudo chmod 644 "$FILE_PATH"
|
||
|
||
echo "File created with permissions 644."
|
||
fi
|
||
|
||
# Verify the file and permissions
|
||
if [ -f "$FILE_PATH" ]; then
|
||
FINAL_PERMS=$(stat -f "%Lp" "$FILE_PATH" 2>/dev/null)
|
||
echo "✓ File exists at: $FILE_PATH"
|
||
echo "✓ Permissions: $FINAL_PERMS"
|
||
else
|
||
echo "✗ Error: Failed to create file"
|
||
exit 1
|
||
fi
|
||
|
||
|
||
- name: Refetch host
|
||
platform: macos
|
||
description: A 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.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
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 "$@"
|
||
|
||
- name: Uninstall fleetd
|
||
platform: macos
|
||
description: Removes the fleetd agent from a macOS device.
|
||
script: |
|
||
#!/bin/sh
|
||
# Please don't delete. This script is referenced in the guide here: https://fleetdm.com/guides/how-to-uninstall-fleetd
|
||
|
||
if [ $(id -u) -ne 0 ]; then
|
||
echo "Please run as root"
|
||
exit 1
|
||
fi
|
||
|
||
function remove_fleet {
|
||
set -x
|
||
rm -rf /Library/LaunchDaemons/com.fleetdm.orbit.plist /var/lib/orbit /usr/local/bin/orbit /var/log/orbit /opt/orbit/
|
||
pkgutil --forget com.fleetdm.orbit.base.pkg || true
|
||
launchctl stop com.fleetdm.orbit
|
||
launchctl unload /Library/LaunchDaemons/com.fleetdm.orbit.plist
|
||
pkill fleet-desktop || true
|
||
|
||
# Check MDM status on a macOS device
|
||
mdm_status=$(profiles status -type enrollment)
|
||
|
||
# Check for MDM enrollment status and cleanup enrollment profile
|
||
if echo "$mdm_status" | grep -q "MDM enrollment: Yes"; then
|
||
echo "This Mac is MDM enrolled. Removing enrollment profile."
|
||
profiles remove -identifier com.fleetdm.fleet.mdm.apple
|
||
elif echo "$mdm_status" | grep -q "MDM enrollment: No"; then
|
||
echo "This Mac is not MDM enrolled."
|
||
else
|
||
echo "MDM status is unknown."
|
||
fi
|
||
}
|
||
|
||
if [ "$1" = "remove" ]; then
|
||
# We are in the detached child process
|
||
# Give the parent process time to report the success before removing
|
||
echo "inside remove process" >>/tmp/fleet_remove_log.txt
|
||
sleep 15
|
||
|
||
# We are root
|
||
remove_fleet >>/tmp/fleet_remove_log.txt 2>&1
|
||
else
|
||
# We are in the parent shell, start the detached child and return success
|
||
echo "Removing fleetd, system will be unenrolled in 15 seconds..."
|
||
echo "Executing detached child process"
|
||
|
||
# We are root
|
||
bash -c "bash $0 remove >/dev/null 2>/dev/null </dev/null &"
|
||
fi
|
||
|
||
- name: Disable Wi-Fi auto-join
|
||
platform: macos
|
||
description: Disables auto-join for a specified Wi-Fi network (SSID). Update the SSID variable to match the target network name.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
# Disable Wi-Fi auto-join for a specified SSID (ie, the network name string on a Mac)
|
||
# Based on Alan Siu's approach: https://www.alansiu.net/2026/01/22/scripting-disabling-auto-join-for-wi-fi-networks/
|
||
|
||
/usr/sbin/ipconfig setverbose 1
|
||
ntwknm="$(/usr/sbin/ipconfig getsummary "$(/usr/sbin/scutil --nwi | /usr/bin/awk '/Network interfaces:/{print $NF}')" | /usr/bin/awk -v ORS="," -F ' : ' '!/<dictionary>/&&!/<array>/&&!/}/{gsub("^[[:space:]]*","",$0);print "\x22"$1"\x22"":""\x22"$2"\x22"}' | /usr/bin/sed 's/^/{/;s/,$/}/' | /usr/bin/jq -r '.SSID')"
|
||
/usr/sbin/ipconfig setverbose 0
|
||
|
||
/usr/bin/defaults write /Library/Preferences/com.apple.wifi.known-networks wifi.network.ssid."$ntwknm" -dict-add AutoJoinDisabled -bool TRUE
|
||
|
||
#
|
||
# ╦ ╦╦╔╗╔╔╦╗╔═╗╦ ╦╔═╗
|
||
# ║║║║║║║ ║║║ ║║║║╚═╗
|
||
# ╚╩╝╩╝╚╝═╩╝╚═╝╚╩╝╚═╝
|
||
- name: Create admin user
|
||
platform: windows
|
||
description: Creates an Admin user on Windows device.
|
||
script: |
|
||
$Username = "IT admin"
|
||
$Password = ConvertTo-SecureString "StrongPassword123!" -AsPlainText -Force
|
||
|
||
# Create the local user account
|
||
New-LocalUser -Name $Username -Password $Password -FullName "Fleet IT admin"
|
||
-Description "Admin account used to login when the end user forgets their
|
||
password or the host is returned to Fleet."
|
||
-AccountNeverExpires
|
||
|
||
# Add the user to the Administrators group
|
||
Add-LocalGroupMember -Group "Administrators" -Member $Username
|
||
|
||
- name: Enable Windows defender
|
||
platform: windows
|
||
description: Enable Windows Defender anti-virus.
|
||
script: |
|
||
# Based on commands found here: https://support.huntress.io/hc/en-us/articles/4402989131283-Enable-Microsoft-Defender-via-PowerShell
|
||
# Enable Real-Time Monitoring
|
||
Set-MpPreference -DisableRealtimeMonitoring $false
|
||
|
||
# Enable IOAV Protection
|
||
Set-MpPreference -DisableIOAVProtection $false
|
||
|
||
# Create Registry Key for Real-Time Protection
|
||
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "Real-Time Protection" -Force
|
||
|
||
# Enable Behavior Monitoring
|
||
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableBehaviorMonitoring" -Value 0 -PropertyType DWORD -Force
|
||
|
||
# Enable On-Access Protection
|
||
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableOnAccessProtection" -Value 0 -PropertyType DWORD -Force
|
||
|
||
# Ensure Scans Run When Real-Time Protection is Enabled
|
||
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" -Name "DisableScanOnRealtimeEnable" -Value 0 -PropertyType DWORD -Force
|
||
|
||
# Ensure AntiSpyware is Enabled
|
||
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Value 0 -PropertyType DWORD -Force
|
||
|
||
# Start Windows Defender Services
|
||
Start-Service -Name WinDefend
|
||
Start-Service -Name WdNisSvc
|
||
|
||
- name: Turn off Windows MDM
|
||
platform: windows
|
||
description: Unenrolls a Windows device from an MDM solution.
|
||
script: |
|
||
Add-Type -TypeDefinition @"
|
||
using System;
|
||
using System.Runtime.InteropServices;
|
||
|
||
public class MdmRegistration
|
||
{
|
||
[DllImport("mdmregistration.dll", SetLastError = true)]
|
||
public static extern int UnregisterDeviceWithManagement(IntPtr pDeviceID);
|
||
|
||
public static int UnregisterDevice()
|
||
{
|
||
return UnregisterDeviceWithManagement(IntPtr.Zero);
|
||
}
|
||
}
|
||
"@ -Language CSharp
|
||
|
||
try {
|
||
$result = [MdmRegistration]::UnregisterDevice()
|
||
|
||
if ($result -ne 0) {
|
||
throw "UnregisterDeviceWithManagement failed with error code: $result"
|
||
}
|
||
|
||
Write-Host "Device unregistration called successfully."
|
||
} catch {
|
||
Write-Error "Error calling UnregisterDeviceWithManagement: $_"
|
||
}
|
||
|
||
- name: Uninstall fleetd
|
||
platform: windows
|
||
description: Removes the fleetd agent from a Windows device.
|
||
script: |
|
||
function Test-Administrator
|
||
{
|
||
[OutputType([bool])]
|
||
param()
|
||
process {
|
||
[Security.Principal.WindowsPrincipal]$user = [Security.Principal.WindowsIdentity]::GetCurrent();
|
||
return $user.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator);
|
||
}
|
||
}
|
||
|
||
# borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx
|
||
function Resolve-Error-Detailed($ErrorRecord = $Error[0]) {
|
||
$error_message = "========== ErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:{2}"
|
||
$formatted_errorRecord = $ErrorRecord | format-list * -force | out-string
|
||
$formatted_invocationInfo = $ErrorRecord.InvocationInfo | format-list * -force | out-string
|
||
$formatted_exception = ""
|
||
$Exception = $ErrorRecord.Exception
|
||
for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) {
|
||
$formatted_exception += ("$i" * 70) + "-----"
|
||
$formatted_exception += $Exception | format-list * -force | out-string
|
||
$formatted_exception += "-----"
|
||
}
|
||
|
||
return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception
|
||
}
|
||
|
||
#Stops Orbit service and related processes
|
||
function Stop-Orbit {
|
||
# Stop Service
|
||
Stop-Service -Name "Fleet osquery" -ErrorAction "Continue"
|
||
Start-Sleep -Milliseconds 1000
|
||
|
||
# Ensure that no process left running
|
||
Get-Process -Name "orbit" -ErrorAction "SilentlyContinue" | Stop-Process -Force
|
||
Get-Process -Name "osqueryd" -ErrorAction "SilentlyContinue" | Stop-Process -Force
|
||
Get-Process -Name "fleet-desktop" -ErrorAction "SilentlyContinue" | Stop-Process -Force
|
||
Start-Sleep -Milliseconds 1000
|
||
}
|
||
|
||
#Remove Orbit footprint from registry and disk
|
||
function Force-Remove-Orbit {
|
||
try {
|
||
#Stoping Orbit
|
||
Stop-Orbit
|
||
|
||
#Remove Service
|
||
$service = Get-WmiObject -Class Win32_Service -Filter "Name='Fleet osquery'"
|
||
if ($service) {
|
||
$service.delete() | Out-Null
|
||
}
|
||
|
||
#Removing Program files entries
|
||
$targetPath = $Env:Programfiles + "\\Orbit"
|
||
Remove-Item -LiteralPath $targetPath -Force -Recurse -ErrorAction "Continue"
|
||
|
||
#Remove HKLM registry entries
|
||
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse -ErrorAction "SilentlyContinue" | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object {
|
||
# Filter for osquery entries
|
||
$properties = Get-ItemProperty $_.PSPath -ErrorAction "SilentlyContinue" | Where-Object {($_.DisplayName -eq "Fleet osquery")}
|
||
if ($properties) {
|
||
#Remove Registry Entries
|
||
$regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName
|
||
Get-Item $regKey -ErrorAction "SilentlyContinue" | Remove-Item -Force -ErrorAction "SilentlyContinue"
|
||
return
|
||
}
|
||
}
|
||
|
||
# Write success log
|
||
"Fleetd successfully removed at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
|
||
}
|
||
catch {
|
||
Write-Host "There was a problem running Force-Remove-Orbit"
|
||
Write-Host "$(Resolve-Error-Detailed)"
|
||
# Write error log
|
||
"Error removing fleetd at $(Get-Date): $($Error[0])" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
|
||
return $false
|
||
}
|
||
|
||
return $true
|
||
}
|
||
|
||
function Main {
|
||
try {
|
||
# Is Administrator check
|
||
if (-not (Test-Administrator)) {
|
||
Write-Host "Please run this script with admin privileges."
|
||
Exit -1
|
||
}
|
||
|
||
Write-Host "About to uninstall fleetd..."
|
||
|
||
if ($args[0] -eq "remove") {
|
||
# "remove" is received as argument to the script when called as the
|
||
# sub-process that will actually remove the fleet agent.
|
||
|
||
# Log the start of removal process
|
||
"Starting removal process at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
|
||
|
||
# sleep to give time to fleetd to send the script results to Fleet
|
||
Start-Sleep -Seconds 20
|
||
|
||
if (Force-Remove-Orbit) {
|
||
Write-Host "fleetd was uninstalled."
|
||
Exit 0
|
||
} else {
|
||
Write-Host "There was a problem uninstalling fleetd."
|
||
Exit -1
|
||
}
|
||
} else {
|
||
# when this script is executed from fleetd, it does not immediately
|
||
# remove the agent. Instead, it starts a new detached process that
|
||
# will do the actual removal.
|
||
|
||
Write-Host "Removing fleetd, system will be unenrolled in 20 seconds..."
|
||
Write-Host "Executing detached child process"
|
||
|
||
$execName = $MyInvocation.ScriptName
|
||
$proc = Start-Process -PassThru -FilePath "powershell" -WindowStyle Hidden -ArgumentList "-MTA", "-ExecutionPolicy", "Bypass", "-File", "$execName remove"
|
||
|
||
# Log the process ID
|
||
"Started removal process with ID: $($proc.Id) at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
|
||
|
||
Start-Sleep -Seconds 5 # give time to process to start running
|
||
Write-Host "Removal process started: $($proc.Id)."
|
||
}
|
||
} catch {
|
||
Write-Host "Error: Entry point"
|
||
Write-Host "$(Resolve-Error-Detailed)"
|
||
Exit -1
|
||
}
|
||
}
|
||
|
||
# Execute the script with arguments passed to it
|
||
Main $args[0]
|
||
|
||
- name: Uninstall Slack
|
||
platform: windows
|
||
description: Removes Slack from a Windows device.
|
||
script: |
|
||
# Slack Uninstall Script
|
||
# This script handles both MSI and EXE installations, including per-user installations
|
||
|
||
$softwareName = "Slack"
|
||
$exitCode = 0
|
||
$uninstalled = $false
|
||
|
||
Write-Host "Starting Slack uninstallation process..."
|
||
|
||
# Function to uninstall MSI packages
|
||
function Remove-SlackMSI {
|
||
Write-Host "Checking for MSI-based Slack installations..."
|
||
|
||
# Find all Slack MSI products
|
||
$msiProducts = Get-WmiObject -Class Win32_Product -Filter "Name LIKE '%Slack%'" -ErrorAction SilentlyContinue
|
||
|
||
if ($msiProducts) {
|
||
foreach ($product in $msiProducts) {
|
||
Write-Host "Found MSI: $($product.Name) - Version: $($product.Version)"
|
||
Write-Host "Attempting to uninstall MSI..."
|
||
|
||
try {
|
||
$result = $product.Uninstall()
|
||
if ($result.ReturnValue -eq 0) {
|
||
Write-Host "Successfully uninstalled MSI: $($product.Name)"
|
||
return $true
|
||
} else {
|
||
Write-Host "MSI uninstall returned code: $($result.ReturnValue)"
|
||
}
|
||
} catch {
|
||
Write-Host "Error uninstalling MSI: $_"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Also try using msiexec with product codes from registry
|
||
$msiKeys = @(
|
||
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
||
)
|
||
|
||
foreach ($keyPath in $msiKeys) {
|
||
$keys = Get-ChildItem -Path $keyPath -ErrorAction SilentlyContinue |
|
||
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }
|
||
|
||
foreach ($key in $keys) {
|
||
if ($key.DisplayName -like "*Slack*" -and $key.PSChildName -match '^{[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}}$') {
|
||
Write-Host "Found MSI product code: $($key.PSChildName)"
|
||
Write-Host "Attempting msiexec uninstall..."
|
||
|
||
$msiArgs = @("/x", $key.PSChildName, "/qn", "/norestart", "REBOOT=ReallySuppress")
|
||
$process = Start-Process -FilePath "msiexec.exe" -ArgumentList $msiArgs -Wait -PassThru -NoNewWindow
|
||
|
||
if ($process.ExitCode -eq 0) {
|
||
Write-Host "Successfully uninstalled via msiexec"
|
||
return $true
|
||
} else {
|
||
Write-Host "msiexec returned exit code: $($process.ExitCode)"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return $false
|
||
}
|
||
|
||
# Function to uninstall EXE-based installations
|
||
function Remove-SlackEXE {
|
||
Write-Host "Checking for EXE-based Slack installations..."
|
||
|
||
$uninstallKeys = @(
|
||
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
||
)
|
||
|
||
$foundAny = $false
|
||
|
||
foreach ($keyPath in $uninstallKeys) {
|
||
$keys = Get-ChildItem -Path $keyPath -ErrorAction SilentlyContinue |
|
||
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }
|
||
|
||
foreach ($key in $keys) {
|
||
if ($key.DisplayName -like "*Slack*") {
|
||
$foundAny = $true
|
||
Write-Host "Found: $($key.DisplayName) at $($keyPath)"
|
||
|
||
if ($key.UninstallString) {
|
||
# Extract the executable path and arguments
|
||
$uninstallString = $key.UninstallString
|
||
$exePath = ""
|
||
$arguments = ""
|
||
|
||
if ($uninstallString -match '^"([^"]+)"(.*)') {
|
||
$exePath = $matches[1]
|
||
$arguments = $matches[2].Trim()
|
||
} elseif ($uninstallString -match '^([^\s]+)(.*)') {
|
||
$exePath = $matches[1]
|
||
$arguments = $matches[2].Trim()
|
||
}
|
||
|
||
Write-Host "Uninstall executable: $exePath"
|
||
|
||
# For Slack, common silent parameters
|
||
$silentParams = @(
|
||
"--uninstall --force-uninstall",
|
||
"--uninstall",
|
||
"/S",
|
||
"/SILENT",
|
||
"-s"
|
||
)
|
||
|
||
# First try QuietUninstallString if available
|
||
if ($key.QuietUninstallString) {
|
||
Write-Host "Trying QuietUninstallString..."
|
||
$process = Start-Process -FilePath "cmd.exe" -ArgumentList "/c `"$($key.QuietUninstallString)`"" -Wait -PassThru -NoNewWindow
|
||
if ($process.ExitCode -eq 0) {
|
||
Write-Host "Successfully uninstalled using QuietUninstallString"
|
||
return $true
|
||
}
|
||
}
|
||
|
||
# Try each silent parameter
|
||
foreach ($param in $silentParams) {
|
||
Write-Host "Trying with parameters: $param"
|
||
|
||
try {
|
||
$fullArgs = if ($arguments) { "$arguments $param" } else { $param }
|
||
$process = Start-Process -FilePath $exePath -ArgumentList $fullArgs -Wait -PassThru -NoNewWindow -ErrorAction Stop
|
||
|
||
if ($process.ExitCode -eq 0) {
|
||
Write-Host "Successfully uninstalled with parameters: $param"
|
||
return $true
|
||
} else {
|
||
Write-Host "Exit code: $($process.ExitCode)"
|
||
}
|
||
} catch {
|
||
Write-Host "Error: $_"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (-not $foundAny) {
|
||
Write-Host "No EXE-based Slack installations found in registry"
|
||
}
|
||
|
||
return $false
|
||
}
|
||
|
||
# Function to kill Slack processes
|
||
function Stop-SlackProcesses {
|
||
Write-Host "Checking for running Slack processes..."
|
||
$processes = Get-Process -Name "Slack*" -ErrorAction SilentlyContinue
|
||
|
||
if ($processes) {
|
||
Write-Host "Found $($processes.Count) Slack process(es). Attempting to stop..."
|
||
foreach ($proc in $processes) {
|
||
try {
|
||
$proc | Stop-Process -Force -ErrorAction Stop
|
||
Write-Host "Stopped process: $($proc.ProcessName) (PID: $($proc.Id))"
|
||
} catch {
|
||
Write-Host "Failed to stop process: $($proc.ProcessName) - $_"
|
||
}
|
||
}
|
||
Start-Sleep -Seconds 2
|
||
}
|
||
}
|
||
|
||
# Function to clean up Slack folders
|
||
function Remove-SlackFolders {
|
||
Write-Host "Cleaning up Slack folders..."
|
||
|
||
$foldersToRemove = @(
|
||
"$env:LOCALAPPDATA\Slack",
|
||
"$env:APPDATA\Slack",
|
||
"$env:ProgramFiles\Slack",
|
||
"${env:ProgramFiles(x86)}\Slack"
|
||
)
|
||
|
||
foreach ($folder in $foldersToRemove) {
|
||
if (Test-Path $folder) {
|
||
Write-Host "Removing folder: $folder"
|
||
try {
|
||
Remove-Item -Path $folder -Recurse -Force -ErrorAction Stop
|
||
Write-Host "Successfully removed: $folder"
|
||
} catch {
|
||
Write-Host "Failed to remove folder: $_"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# Main uninstallation logic
|
||
try {
|
||
# Stop Slack processes first
|
||
Stop-SlackProcesses
|
||
|
||
# Try MSI uninstallation first
|
||
$msiResult = Remove-SlackMSI
|
||
if ($msiResult) {
|
||
$uninstalled = $true
|
||
Write-Host "Slack uninstalled via MSI method"
|
||
}
|
||
|
||
# Try EXE uninstallation
|
||
$exeResult = Remove-SlackEXE
|
||
if ($exeResult) {
|
||
$uninstalled = $true
|
||
Write-Host "Slack uninstalled via EXE method"
|
||
}
|
||
|
||
# Clean up folders regardless of uninstall method success
|
||
Remove-SlackFolders
|
||
|
||
# Final verification
|
||
Start-Sleep -Seconds 3
|
||
$remainingInstalls = @(
|
||
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
|
||
Where-Object { $_.DisplayName -like "*Slack*" }
|
||
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
|
||
Where-Object { $_.DisplayName -like "*Slack*" }
|
||
Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
|
||
Where-Object { $_.DisplayName -like "*Slack*" }
|
||
)
|
||
|
||
if ($remainingInstalls.Count -eq 0) {
|
||
Write-Host "Verification: No Slack installations found in registry"
|
||
$exitCode = 0
|
||
} elseif ($uninstalled) {
|
||
Write-Host "Warning: Some Slack registry entries remain, but uninstallation was attempted"
|
||
$exitCode = 0
|
||
} else {
|
||
Write-Host "Error: Slack uninstallation failed"
|
||
$exitCode = 1
|
||
}
|
||
|
||
} catch {
|
||
Write-Host "Critical error during uninstallation: $_"
|
||
$exitCode = 1
|
||
}
|
||
|
||
Write-Host "Slack uninstallation script completed with exit code: $exitCode"
|
||
Exit $exitCode
|
||
|
||
- name: Uninstall Zoom
|
||
platform: windows
|
||
description: Removes Zoom from a Windows device.
|
||
script: |
|
||
# Zoom Uninstall Script for Fleet
|
||
|
||
$exitCode = 0
|
||
|
||
# Kill Zoom processes
|
||
Get-Process -Name "Zoom*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||
Start-Sleep -Seconds 2
|
||
|
||
# Method 1: Try CleanZoom.exe if provided with the script
|
||
$cleanZoom = "$PSScriptRoot\CleanZoom.exe"
|
||
if (Test-Path $cleanZoom) {
|
||
Write-Host "Using CleanZoom.exe"
|
||
& $cleanZoom /silent
|
||
$exitCode = $LASTEXITCODE
|
||
Exit $exitCode
|
||
}
|
||
|
||
# Method 2: Registry uninstall (both HKLM and HKCU)
|
||
$paths = @(
|
||
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
||
)
|
||
|
||
$found = $false
|
||
foreach ($path in $paths) {
|
||
if (Test-Path $path) {
|
||
Get-ItemProperty $path -ErrorAction SilentlyContinue |
|
||
Where-Object { $_.DisplayName -like "*Zoom*" } |
|
||
ForEach-Object {
|
||
$found = $true
|
||
Write-Host "Uninstalling: $($_.DisplayName)"
|
||
|
||
# Try QuietUninstallString
|
||
if ($_.QuietUninstallString) {
|
||
cmd /c "$($_.QuietUninstallString)" 2>&1 | Out-Null
|
||
if ($LASTEXITCODE -eq 0) { $exitCode = 0; return }
|
||
}
|
||
|
||
# Try UninstallString with silent flags
|
||
if ($_.UninstallString) {
|
||
# Zoom-specific silent uninstall
|
||
cmd /c "$($_.UninstallString) /uninstall /silent" 2>&1 | Out-Null
|
||
if ($LASTEXITCODE -eq 0) { $exitCode = 0; return }
|
||
|
||
# Generic silent
|
||
cmd /c "$($_.UninstallString) /S" 2>&1 | Out-Null
|
||
if ($LASTEXITCODE -eq 0) { $exitCode = 0; return }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (-not $found) {
|
||
Write-Host "Zoom not found"
|
||
$exitCode = 0 # Not found = success for Fleet
|
||
}
|
||
|
||
Exit $exitCode
|
||
|
||
#
|
||
# ╦ ╦╔╗╔╦ ╦═╗ ╦
|
||
# ║ ║║║║║ ║╔╩╦╝
|
||
# ╩═╝╩╝╚╝╚═╝╩ ╚═
|
||
|
||
- name: Uninstall fleetd
|
||
platform: linux
|
||
description: Unintalls the fleetd agent on a Linux device.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
if [ $(id -u) -ne 0 ]; then
|
||
echo "Please run as root"
|
||
exit 1
|
||
fi
|
||
|
||
function remove_fleet {
|
||
set -x
|
||
systemctl stop orbit.service || true
|
||
systemctl disable orbit.service || true
|
||
rm -rf /var/lib/orbit /opt/orbit /var/log/orbit /usr/local/bin/orbit /etc/default/orbit /usr/lib/systemd/system/orbit.service
|
||
|
||
# Remove any package references
|
||
if command -v dpkg > /dev/null; then
|
||
dpkg --purge fleet-osquery || true
|
||
elif command -v rpm > /dev/null; then
|
||
rpm -e fleet-osquery || true
|
||
fi
|
||
|
||
# Kill any running Fleet processes
|
||
pkill -f fleet-desktop || true
|
||
|
||
# Reload systemd configuration
|
||
systemctl daemon-reload
|
||
|
||
echo "Fleetd has been successfully removed from the system."
|
||
}
|
||
|
||
if [ "$1" = "remove" ]; then
|
||
# We are in the detached child process
|
||
# Give the parent process time to report the success before removing
|
||
echo "inside remove process" >>/tmp/fleet_remove_log.txt
|
||
sleep 15
|
||
|
||
# We are root
|
||
remove_fleet >>/tmp/fleet_remove_log.txt 2>&1
|
||
else
|
||
# We are in the parent shell, start the detached child and return success
|
||
echo "Removing fleetd, system will be unenrolled in 15 seconds..."
|
||
echo "Executing detached child process"
|
||
|
||
# We are root
|
||
bash -c "bash $0 remove >/dev/null 2>/dev/null </dev/null &"
|
||
fi
|
||
|
||
- name: Set the root user default group to GID 0
|
||
platform: linux
|
||
description: Set the root user’s primary group to the group with GID 0
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
usermod -g 0 root
|
||
|
||
- name: Configure nftables loopback traffic
|
||
platform: linux
|
||
description: Set up nftables rules to allow legitimate loopback traffic on the lo interface while dropping spoofed loopback packets from external interfaces.
|
||
script: |
|
||
#!/bin/bash
|
||
nft create table inet filter
|
||
nft create chain inet filter input { type filter hook input priority 0 \; }
|
||
nft create chain inet filter output { type filter hook output priority 0 \; }
|
||
|
||
# Then add the loopback rules:
|
||
nft add rule inet filter input iif lo accept
|
||
nft add rule inet filter output oif lo accept
|
||
nft add rule inet filter input ip saddr 127.0.0.0/8 counter drop
|
||
nft add rule inet filter input ip6 saddr ::1 counter drop
|
||
|
||
# To make rules persistent, save them:
|
||
nft list ruleset > /etc/nftables.conf
|
||
systemctl enable nftables
|
||
|
||
- name: Enable auditd service
|
||
platform: linux
|
||
description: Enable and start the auditd service to provide system auditing and logging of security-relevant events.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
systemctl --now enable auditd
|
||
|
||
- name: Set default inactivity period for user accounts
|
||
platform: linux
|
||
description: Configure the default number of days (30) after a password expires before a user account is disabled.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
useradd -D -f 30
|
||
|
||
- name: Set ownership and permissions on GRUB configuration
|
||
platform: linux
|
||
description: Configure /boot/grub/grub.cfg to be owned by root and inaccessible to non-privileged users.
|
||
script: |
|
||
#!/bin/bash
|
||
|
||
chown root:root /boot/grub/grub.cfg
|
||
chmod og-rwx /boot/grub/grub.cfg
|
||
|