Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Aviso importante

Los ejemplos de código y scripts proporcionados en estos tutoriales son solo para propósitos educativos. Macfleet no es responsable de ningún problema, daño o vulnerabilidad de seguridad que pueda surgir del uso, modificación o implementación de estos ejemplos. Siempre revisa y prueba el código en un entorno seguro antes de usarlo en sistemas de producción.

Code Signature Analysis and Security on macOS

Analyze and manage application code signatures on your MacFleet devices to enhance security, verify application authenticity, and implement enterprise signing policies. This tutorial covers code requirement extraction, signature verification, and comprehensive security analysis.

Understanding macOS Code Signatures

Code signatures on macOS provide cryptographic verification that applications haven't been tampered with and come from trusted sources. Key components include:

  • Code Requirements - Constraints that code must satisfy to be considered valid
  • Bundle Identifiers - Unique identifiers for applications
  • Developer Certificates - Digital certificates used to sign applications
  • Entitlements - Permissions and capabilities granted to applications

Enterprise Security Importance

Code signature analysis is crucial for enterprise security:

  • Application Authenticity - Verify applications come from trusted developers
  • Tampering Detection - Identify modified or compromised applications
  • Privacy Policy Enforcement - Control access to protected user data
  • Compliance Requirements - Meet security standards for regulated industries
  • Malware Prevention - Block unauthorized or suspicious applications

Basic Code Signature Analysis

Extract Code Requirement

#!/bin/bash

# Basic code requirement extraction
APP_PATH="/System/Applications/Time Machine.app"

echo "Extracting code requirement for: $APP_PATH"
codesign -dr - "$APP_PATH"

Extract Code Requirement with Error Handling

#!/bin/bash

# Enhanced code requirement extraction with validation
extract_code_requirement() {
    local app_path="$1"
    
    # Validate input
    if [[ -z "$app_path" ]]; then
        echo "Error: No application path provided"
        echo "Usage: extract_code_requirement <app_path>"
        return 1
    fi
    
    # Check if application exists
    if [[ ! -e "$app_path" ]]; then
        echo "Error: Application not found at path: $app_path"
        return 1
    fi
    
    echo "=== Code Requirement Analysis ==="
    echo "Application: $app_path"
    echo ""
    
    # Extract code requirement
    local code_req
    code_req=$(codesign -dr - "$app_path" 2>&1)
    
    if [[ $? -eq 0 ]]; then
        echo "Code Requirement:"
        echo "$code_req"
        
        # Extract just the designated requirement
        local designated_req
        designated_req=$(echo "$code_req" | grep "designated =>" | sed 's/designated => //')
        
        if [[ -n "$designated_req" ]]; then
            echo ""
            echo "Designated Requirement:"
            echo "$designated_req"
        fi
    else
        echo "Error extracting code requirement:"
        echo "$code_req"
        return 1
    fi
}

# Usage examples
extract_code_requirement "/System/Applications/Safari.app"
extract_code_requirement "/Applications/Microsoft Word.app"

Comprehensive Application Analysis

#!/bin/bash

# Complete application signature analysis
analyze_application_signature() {
    local app_path="$1"
    local detailed="${2:-false}"
    
    if [[ ! -e "$app_path" ]]; then
        echo "Error: Application not found: $app_path"
        return 1
    fi
    
    echo "=== Complete Application Signature Analysis ==="
    echo "Application: $app_path"
    echo "Analysis Date: $(date)"
    echo ""
    
    # Basic information
    echo "--- Basic Information ---"
    if [[ -d "$app_path" ]]; then
        local bundle_id
        bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
        echo "Bundle ID: $bundle_id"
        
        local app_version
        app_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "Unknown")
        echo "Version: $app_version"
    fi
    
    # Code signature verification
    echo ""
    echo "--- Code Signature Verification ---"
    if codesign -v "$app_path" 2>/dev/null; then
        echo "✓ Code signature is valid"
    else
        echo "⚠️ Code signature verification failed"
    fi
    
    # Detailed signature information
    echo ""
    echo "--- Signature Details ---"
    codesign -dv "$app_path" 2>&1
    
    # Code requirements
    echo ""
    echo "--- Code Requirements ---"
    codesign -dr - "$app_path" 2>&1
    
    # Entitlements (if detailed analysis requested)
    if [[ "$detailed" == "true" ]]; then
        echo ""
        echo "--- Entitlements ---"
        codesign -d --entitlements - "$app_path" 2>/dev/null || echo "No entitlements found"
    fi
    
    echo ""
    echo "=== Analysis Complete ==="
}

# Usage
analyze_application_signature "/Applications/Safari.app" "true"

Enterprise Code Signature Management

Batch Application Analysis

#!/bin/bash

# Analyze multiple applications for enterprise security review
batch_analyze_applications() {
    local scan_path="${1:-/Applications}"
    local output_format="${2:-table}"
    local include_system="${3:-false}"
    
    echo "=== Batch Application Signature Analysis ==="
    echo "Scan Path: $scan_path"
    echo "Include System Apps: $include_system"
    echo "Output Format: $output_format"
    echo ""
    
    local app_count=0
    local valid_count=0
    local invalid_count=0
    local results=()
    
    # Find all applications
    local find_paths=("$scan_path")
    if [[ "$include_system" == "true" ]]; then
        find_paths+=("/System/Applications" "/System/Library/CoreServices")
    fi
    
    for search_path in "${find_paths[@]}"; do
        if [[ -d "$search_path" ]]; then
            while IFS= read -r -d '' app_path; do
                ((app_count++))
                
                local app_name=$(basename "$app_path" .app)
                local bundle_id="Unknown"
                local signature_status="Invalid"
                local developer="Unknown"
                local code_req="Unknown"
                
                # Extract bundle ID
                if [[ -f "$app_path/Contents/Info.plist" ]]; then
                    bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
                fi
                
                # Check signature validity
                if codesign -v "$app_path" 2>/dev/null; then
                    signature_status="Valid"
                    ((valid_count++))
                    
                    # Extract developer information
                    local sig_info
                    sig_info=$(codesign -dv "$app_path" 2>&1)
                    developer=$(echo "$sig_info" | grep "Authority=" | head -1 | sed 's/Authority=//' | tr -d '\n')
                    
                    # Extract code requirement
                    code_req=$(codesign -dr - "$app_path" 2>/dev/null | grep "designated =>" | sed 's/designated => //')
                else
                    ((invalid_count++))
                fi
                
                # Store results
                results+=("$app_name|$bundle_id|$signature_status|$developer|$code_req")
                
                # Progress indicator
                if (( app_count % 10 == 0 )); then
                    echo "Analyzed $app_count applications..."
                fi
                
            done < <(find "$search_path" -name "*.app" -type d -print0 2>/dev/null)
        fi
    done
    
    echo ""
    echo "=== Analysis Summary ==="
    echo "Total Applications: $app_count"
    echo "Valid Signatures: $valid_count"
    echo "Invalid Signatures: $invalid_count"
    echo ""
    
    # Output results in requested format
    case "$output_format" in
        "table")
            printf "%-30s %-40s %-10s %-50s\n" "Application" "Bundle ID" "Signature" "Developer"
            printf "%-30s %-40s %-10s %-50s\n" "----------" "---------" "---------" "---------"
            
            for result in "${results[@]}"; do
                IFS='|' read -r app_name bundle_id sig_status developer code_req <<< "$result"
                printf "%-30s %-40s %-10s %-50s\n" \
                    "${app_name:0:29}" \
                    "${bundle_id:0:39}" \
                    "$sig_status" \
                    "${developer:0:49}"
            done
            ;;
        "csv")
            echo "Application,Bundle ID,Signature Status,Developer,Code Requirement"
            for result in "${results[@]}"; do
                echo "$result" | tr '|' ','
            done
            ;;
        "json")
            echo "{"
            echo "  \"analysis_summary\": {"
            echo "    \"total_applications\": $app_count,"
            echo "    \"valid_signatures\": $valid_count,"
            echo "    \"invalid_signatures\": $invalid_count,"
            echo "    \"scan_date\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
            echo "  },"
            echo "  \"applications\": ["
            
            local first=true
            for result in "${results[@]}"; do
                IFS='|' read -r app_name bundle_id sig_status developer code_req <<< "$result"
                
                if [[ "$first" == "true" ]]; then
                    first=false
                else
                    echo ","
                fi
                
                echo -n "    {"
                echo -n "\"name\": \"$app_name\", "
                echo -n "\"bundle_id\": \"$bundle_id\", "
                echo -n "\"signature_valid\": $([ "$sig_status" == "Valid" ] && echo "true" || echo "false"), "
                echo -n "\"developer\": \"$developer\", "
                echo -n "\"code_requirement\": \"$code_req\""
                echo -n "}"
            done
            
            echo ""
            echo "  ]"
            echo "}"
            ;;
    esac
}

# Usage examples
batch_analyze_applications "/Applications" "table" "false"
batch_analyze_applications "/Applications" "json" "true" > /tmp/app_analysis.json

Security Policy Enforcement

#!/bin/bash

# Enforce enterprise code signing policies
enforce_code_signing_policy() {
    local policy_name="$1"
    local action="${2:-report}"
    local target_path="${3:-/Applications}"
    
    echo "=== Enforcing Code Signing Policy: $policy_name ==="
    echo "Action: $action"
    echo "Target Path: $target_path"
    echo ""
    
    local violations=()
    local compliant_apps=()
    
    case "$policy_name" in
        "enterprise_strict")
            echo "Policy: Enterprise Strict - Only signed applications from trusted developers"
            
            # Define trusted developers
            local trusted_developers=(
                "Apple Inc."
                "Microsoft Corporation"
                "Adobe Inc."
                "Google LLC"
                "Zoom Video Communications, Inc."
            )
            
            check_enterprise_strict_policy "$target_path" trusted_developers violations compliant_apps
            ;;
        "apple_signed_only")
            echo "Policy: Apple Signed Only - Only Apple-signed applications allowed"
            check_apple_signed_policy "$target_path" violations compliant_apps
            ;;
        "no_unsigned")
            echo "Policy: No Unsigned - All applications must be signed"
            check_no_unsigned_policy "$target_path" violations compliant_apps
            ;;
        "developer_id_required")
            echo "Policy: Developer ID Required - All apps must have valid Developer ID"
            check_developer_id_policy "$target_path" violations compliant_apps
            ;;
        *)
            echo "Error: Unknown policy '$policy_name'"
            echo "Available policies: enterprise_strict, apple_signed_only, no_unsigned, developer_id_required"
            return 1
            ;;
    esac
    
    # Report results
    echo ""
    echo "=== Policy Enforcement Results ==="
    echo "Compliant Applications: ${#compliant_apps[@]}"
    echo "Policy Violations: ${#violations[@]}"
    
    if [[ ${#violations[@]} -gt 0 ]]; then
        echo ""
        echo "Policy Violations Found:"
        for violation in "${violations[@]}"; do
            echo "  ⚠️ $violation"
        done
    fi
    
    # Take action based on policy
    case "$action" in
        "report")
            echo ""
            echo "Action: Report Only - No changes made"
            generate_policy_report "$policy_name" violations compliant_apps
            ;;
        "quarantine")
            echo ""
            echo "Action: Quarantine - Moving violating applications"
            quarantine_violations violations
            ;;
        "remove")
            echo ""
            echo "Action: Remove - Deleting violating applications"
            remove_violations violations
            ;;
        *)
            echo "Error: Unknown action '$action'"
            ;;
    esac
}

# Check enterprise strict policy
check_enterprise_strict_policy() {
    local target_path="$1"
    local -n trusted_devs=$2
    local -n violations_ref=$3
    local -n compliant_ref=$4
    
    while IFS= read -r -d '' app_path; do
        local app_name=$(basename "$app_path")
        
        if codesign -v "$app_path" 2>/dev/null; then
            local developer
            developer=$(codesign -dv "$app_path" 2>&1 | grep "Authority=" | head -1 | sed 's/Authority=//')
            
            local is_trusted=false
            for trusted_dev in "${trusted_devs[@]}"; do
                if [[ "$developer" == *"$trusted_dev"* ]]; then
                    is_trusted=true
                    break
                fi
            done
            
            if [[ "$is_trusted" == "true" ]]; then
                compliant_ref+=("$app_name")
            else
                violations_ref+=("$app_name (Developer: $developer)")
            fi
        else
            violations_ref+=("$app_name (Unsigned)")
        fi
    done < <(find "$target_path" -name "*.app" -type d -print0 2>/dev/null)
}

# Check Apple-signed only policy
check_apple_signed_policy() {
    local target_path="$1"
    local -n violations_ref=$2
    local -n compliant_ref=$3
    
    while IFS= read -r -d '' app_path; do
        local app_name=$(basename "$app_path")
        
        if codesign -v "$app_path" 2>/dev/null; then
            local developer
            developer=$(codesign -dv "$app_path" 2>&1 | grep "Authority=" | head -1 | sed 's/Authority=//')
            
            if [[ "$developer" == *"Apple"* ]]; then
                compliant_ref+=("$app_name")
            else
                violations_ref+=("$app_name (Non-Apple Developer: $developer)")
            fi
        else
            violations_ref+=("$app_name (Unsigned)")
        fi
    done < <(find "$target_path" -name "*.app" -type d -print0 2>/dev/null)
}

# Generate policy compliance report
generate_policy_report() {
    local policy_name="$1"
    local -n violations_ref=$2
    local -n compliant_ref=$3
    
    local report_file="/tmp/code_signing_policy_report_$(date +%Y%m%d_%H%M%S).json"
    
    cat > "$report_file" << EOF
{
    "code_signing_policy_report": {
        "policy_name": "$policy_name",
        "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
        "hostname": "$(hostname)",
        "summary": {
            "total_applications": $((${#violations_ref[@]} + ${#compliant_ref[@]})),
            "compliant_applications": ${#compliant_ref[@]},
            "policy_violations": ${#violations_ref[@]},
            "compliance_percentage": $(( ${#compliant_ref[@]} * 100 / (${#violations_ref[@]} + ${#compliant_ref[@]}) ))
        },
        "violations": [
EOF
    
    local first=true
    for violation in "${violations_ref[@]}"; do
        if [[ "$first" == "true" ]]; then
            first=false
        else
            echo "," >> "$report_file"
        fi
        echo -n "            \"$violation\"" >> "$report_file"
    done
    
    cat >> "$report_file" << EOF

        ],
        "compliant_applications": [
EOF
    
    first=true
    for compliant in "${compliant_ref[@]}"; do
        if [[ "$first" == "true" ]]; then
            first=false
        else
            echo "," >> "$report_file"
        fi
        echo -n "            \"$compliant\"" >> "$report_file"
    done
    
    cat >> "$report_file" << EOF

        ]
    }
}
EOF
    
    echo "Policy report generated: $report_file"
}

# Usage
enforce_code_signing_policy "enterprise_strict" "report" "/Applications"

Enterprise Code Signature Management System

#!/bin/bash

# MacFleet Code Signature Management Tool
# Comprehensive code signature analysis and security policy enforcement

# Configuration
SCRIPT_VERSION="1.0.0"
LOG_FILE="/var/log/macfleet_codesigning.log"
REPORT_DIR="/etc/macfleet/reports/codesigning"
CONFIG_DIR="/etc/macfleet/codesigning"
POLICY_DIR="/etc/macfleet/policies/codesigning"
QUARANTINE_DIR="/var/quarantine/applications"

# Create directories if they don't exist
mkdir -p "$REPORT_DIR" "$CONFIG_DIR" "$POLICY_DIR" "$QUARANTINE_DIR"

# Code signing policy templates
declare -A SIGNING_POLICIES=(
    ["enterprise_standard"]="signed_required,developer_id_preferred,apple_signed_trusted,third_party_review"
    ["enterprise_strict"]="apple_signed_only,developer_id_required,no_unsigned,enterprise_review"
    ["development"]="signed_preferred,developer_id_optional,self_signed_allowed,minimal_restrictions"
    ["kiosk_lockdown"]="apple_signed_only,no_third_party,system_apps_only,maximum_security"
    ["healthcare"]="apple_signed_required,enterprise_approved_only,no_unsigned,hipaa_compliant"
    ["financial"]="apple_signed_required,enterprise_certified_only,strict_validation,sox_compliant"
    ["education"]="signed_required,educational_approved,apple_preferred,moderate_security"
    ["government"]="apple_signed_only,government_approved,no_unsigned,maximum_security"
    ["retail"]="signed_required,pos_approved,apple_preferred,moderate_security"
    ["manufacturing"]="signed_required,industrial_approved,apple_preferred,operational_security"
)

# Trusted developer registry
declare -A TRUSTED_DEVELOPERS=(
    ["apple"]="Apple Inc.,Software Signing,Apple Mac OS Application Signing"
    ["microsoft"]="Microsoft Corporation,Microsoft Corporation"
    ["adobe"]="Adobe Inc.,Adobe Systems Incorporated"
    ["google"]="Google LLC,Google Inc."
    ["zoom"]="Zoom Video Communications, Inc."
    ["slack"]="Slack Technologies, Inc."
    ["dropbox"]="Dropbox, Inc."
    ["1password"]="AgileBits Inc."
    ["firefox"]="Mozilla Corporation"
    ["chrome"]="Google LLC"
)

# Security classification levels
declare -A SECURITY_LEVELS=(
    ["maximum"]="apple_signed_only,no_exceptions,immediate_quarantine"
    ["high"]="signed_required,trusted_developers_only,review_required"
    ["medium"]="signed_preferred,warnings_enabled,monitoring_active"
    ["low"]="unsigned_allowed,notification_only,basic_monitoring"
)

# Logging function
log_action() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] $message" | tee -a "$LOG_FILE"
}

# Advanced code signature analysis
analyze_code_signature() {
    local app_path="$1"
    local output_format="${2:-detailed}"
    local security_check="${3:-true}"
    
    log_action "Analyzing code signature for: $app_path"
    
    if [[ ! -e "$app_path" ]]; then
        log_action "ERROR: Application not found: $app_path"
        return 1
    fi
    
    local analysis_data=()
    local app_name=$(basename "$app_path" .app)
    
    echo "=== Advanced Code Signature Analysis ==="
    echo "Application: $app_name"
    echo "Path: $app_path"
    echo "Analysis Date: $(date)"
    echo ""
    
    # Basic application information
    extract_app_metadata "$app_path" analysis_data
    
    # Code signature verification
    verify_code_signature "$app_path" analysis_data
    
    # Extract code requirements
    extract_code_requirements "$app_path" analysis_data
    
    # Security analysis
    if [[ "$security_check" == "true" ]]; then
        perform_security_analysis "$app_path" analysis_data
    fi
    
    # Generate output in requested format
    case "$output_format" in
        "detailed")
            display_detailed_analysis analysis_data
            ;;
        "summary")
            display_summary_analysis analysis_data
            ;;
        "json")
            generate_json_analysis "$app_path" analysis_data
            ;;
        "csv")
            generate_csv_analysis "$app_path" analysis_data
            ;;
    esac
    
    log_action "Code signature analysis completed for: $app_path"
}

# Extract application metadata
extract_app_metadata() {
    local app_path="$1"
    local -n data_ref=$2
    
    echo "--- Application Metadata ---"
    
    if [[ -f "$app_path/Contents/Info.plist" ]]; then
        local bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
        local version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "Unknown")
        local build=$(defaults read "$app_path/Contents/Info.plist" CFBundleVersion 2>/dev/null || echo "Unknown")
        local min_os=$(defaults read "$app_path/Contents/Info.plist" LSMinimumSystemVersion 2>/dev/null || echo "Unknown")
        
        echo "Bundle ID: $bundle_id"
        echo "Version: $version"
        echo "Build: $build"
        echo "Minimum OS: $min_os"
        
        data_ref+=("bundle_id:$bundle_id")
        data_ref+=("version:$version")
        data_ref+=("build:$build")
        data_ref+=("min_os:$min_os")
    else
        echo "No Info.plist found"
        data_ref+=("bundle_id:Unknown")
    fi
    
    # File size and modification date
    local file_size=$(du -sh "$app_path" | cut -f1)
    local mod_date=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$app_path")
    
    echo "Application Size: $file_size"
    echo "Last Modified: $mod_date"
    
    data_ref+=("size:$file_size")
    data_ref+=("modified:$mod_date")
}

# Verify code signature
verify_code_signature() {
    local app_path="$1"
    local -n data_ref=$2
    
    echo ""
    echo "--- Code Signature Verification ---"
    
    # Basic verification
    if codesign -v "$app_path" 2>/dev/null; then
        echo "✓ Code signature is valid"
        data_ref+=("signature_valid:true")
        
        # Detailed signature information
        local sig_info=$(codesign -dv "$app_path" 2>&1)
        
        # Extract key information
        local identifier=$(echo "$sig_info" | grep "Identifier=" | sed 's/Identifier=//')
        local format=$(echo "$sig_info" | grep "Format=" | sed 's/Format=//')
        local cdhash=$(echo "$sig_info" | grep "CDHash=" | sed 's/CDHash=//')
        local team_id=$(echo "$sig_info" | grep "TeamIdentifier=" | sed 's/TeamIdentifier=//')
        
        echo "Identifier: $identifier"
        echo "Format: $format"
        echo "CDHash: $cdhash"
        echo "Team ID: $team_id"
        
        data_ref+=("identifier:$identifier")
        data_ref+=("format:$format")
        data_ref+=("cdhash:$cdhash")
        data_ref+=("team_id:$team_id")
        
        # Extract certificate chain
        local authorities=$(echo "$sig_info" | grep "Authority=" | sed 's/Authority=//')
        echo "Certificate Chain:"
        while IFS= read -r authority; do
            echo "  - $authority"
        done <<< "$authorities"
        
        data_ref+=("authorities:$authorities")
        
    else
        echo "⚠️ Code signature verification FAILED"
        data_ref+=("signature_valid:false")
        
        local error=$(codesign -v "$app_path" 2>&1)
        echo "Error: $error"
        data_ref+=("signature_error:$error")
    fi
}

# Extract code requirements
extract_code_requirements() {
    local app_path="$1"
    local -n data_ref=$2
    
    echo ""
    echo "--- Code Requirements ---"
    
    local req_output=$(codesign -dr - "$app_path" 2>&1)
    
    if [[ $? -eq 0 ]]; then
        echo "$req_output"
        
        # Extract designated requirement
        local designated_req=$(echo "$req_output" | grep "designated =>" | sed 's/designated => //')
        if [[ -n "$designated_req" ]]; then
            echo ""
            echo "Designated Requirement:"
            echo "$designated_req"
            data_ref+=("designated_requirement:$designated_req")
        fi
        
        data_ref+=("requirements_extracted:true")
    else
        echo "Failed to extract code requirements"
        echo "$req_output"
        data_ref+=("requirements_extracted:false")
    fi
}

# Perform security analysis
perform_security_analysis() {
    local app_path="$1"
    local -n data_ref=$2
    
    echo ""
    echo "--- Security Analysis ---"
    
    local security_score=100
    local security_issues=()
    
    # Check if application is signed
    if ! codesign -v "$app_path" 2>/dev/null; then
        security_score=$((security_score - 50))
        security_issues+=("Application is not signed")
    fi
    
    # Check for hardened runtime
    local flags=$(codesign -dv "$app_path" 2>&1 | grep "CodeDirectory" | grep "runtime")
    if [[ -n "$flags" ]]; then
        echo "✓ Hardened runtime enabled"
        data_ref+=("hardened_runtime:true")
    else
        echo "⚠️ Hardened runtime not enabled"
        security_score=$((security_score - 20))
        security_issues+=("Hardened runtime not enabled")
        data_ref+=("hardened_runtime:false")
    fi
    
    # Check for notarization
    if spctl -a -v "$app_path" 2>&1 | grep -q "notarized"; then
        echo "✓ Application is notarized"
        data_ref+=("notarized:true")
    else
        echo "⚠️ Application is not notarized"
        security_score=$((security_score - 15))
        security_issues+=("Application not notarized")
        data_ref+=("notarized:false")
    fi
    
    # Check for known vulnerabilities (basic check)
    check_known_vulnerabilities "$app_path" security_score security_issues
    
    echo ""
    echo "Security Score: $security_score/100"
    data_ref+=("security_score:$security_score")
    
    if [[ ${#security_issues[@]} -gt 0 ]]; then
        echo "Security Issues Found:"
        for issue in "${security_issues[@]}"; do
            echo "  ⚠️ $issue"
        done
        data_ref+=("security_issues:${security_issues[*]}")
    else
        echo "✓ No security issues found"
        data_ref+=("security_issues:none")
    fi
}

# Check for known vulnerabilities
check_known_vulnerabilities() {
    local app_path="$1"
    local -n score_ref=$2
    local -n issues_ref=$3
    
    local bundle_id
    bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
    
    # Check against known vulnerable applications (simplified example)
    case "$bundle_id" in
        "com.adobe.flashplayer"*)
            score_ref=$((score_ref - 30))
            issues_ref+=("Flash Player has known security vulnerabilities")
            ;;
        "com.java."*)
            # Check Java version for known vulnerabilities
            local java_version
            java_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)
            if [[ -n "$java_version" ]]; then
                echo "Java version detected: $java_version"
                # Add specific version checks here
            fi
            ;;
    esac
    
    # Check for applications with expired certificates
    local cert_info=$(codesign -dv "$app_path" 2>&1 | grep "Timestamp=")
    if [[ -n "$cert_info" ]]; then
        # Extract timestamp and check if certificate is old
        local timestamp=$(echo "$cert_info" | sed 's/Timestamp=//')
        echo "Certificate timestamp: $timestamp"
    fi
}

# Generate comprehensive fleet analysis
analyze_fleet_applications() {
    local scan_paths=("$@")
    local report_file="$REPORT_DIR/fleet_analysis_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Fleet-Wide Application Analysis ==="
    echo "Scanning paths: ${scan_paths[*]}"
    echo ""
    
    local total_apps=0
    local signed_apps=0
    local unsigned_apps=0
    local notarized_apps=0
    local security_issues=0
    local applications=()
    
    for scan_path in "${scan_paths[@]}"; do
        if [[ -d "$scan_path" ]]; then
            echo "Scanning: $scan_path"
            
            while IFS= read -r -d '' app_path; do
                ((total_apps++))
                
                local app_name=$(basename "$app_path" .app)
                local analysis_data=()
                
                # Quick analysis for fleet overview
                analyze_code_signature "$app_path" "summary" "true" > /dev/null
                
                # Extract key metrics
                if codesign -v "$app_path" 2>/dev/null; then
                    ((signed_apps++))
                else
                    ((unsigned_apps++))
                fi
                
                if spctl -a -v "$app_path" 2>&1 | grep -q "notarized"; then
                    ((notarized_apps++))
                fi
                
                # Add to applications array
                applications+=("$app_path")
                
                # Progress indicator
                if (( total_apps % 25 == 0 )); then
                    echo "Analyzed $total_apps applications..."
                fi
                
            done < <(find "$scan_path" -name "*.app" -type d -maxdepth 2 -print0 2>/dev/null)
        fi
    done
    
    # Generate fleet analysis report
    cat > "$report_file" << EOF
{
    "fleet_analysis": {
        "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
        "hostname": "$(hostname)",
        "scan_paths": [$(printf '"%s",' "${scan_paths[@]}" | sed 's/,$//')],
        "summary": {
            "total_applications": $total_apps,
            "signed_applications": $signed_apps,
            "unsigned_applications": $unsigned_apps,
            "notarized_applications": $notarized_apps,
            "signing_compliance": $(( signed_apps * 100 / total_apps )),
            "notarization_compliance": $(( notarized_apps * 100 / total_apps ))
        },
        "analysis_date": "$(date)",
        "script_version": "$SCRIPT_VERSION"
    }
}
EOF
    
    echo ""
    echo "=== Fleet Analysis Summary ==="
    echo "Total Applications: $total_apps"
    echo "Signed Applications: $signed_apps ($(( signed_apps * 100 / total_apps ))%)"
    echo "Unsigned Applications: $unsigned_apps ($(( unsigned_apps * 100 / total_apps ))%)"
    echo "Notarized Applications: $notarized_apps ($(( notarized_apps * 100 / total_apps ))%)"
    echo ""
    echo "Fleet analysis report: $report_file"
    
    log_action "Fleet analysis completed: $total_apps applications analyzed"
}

# Main execution function
main() {
    local action="${1:-help}"
    local param1="${2:-}"
    local param2="${3:-}"
    local param3="${4:-}"
    local param4="${5:-}"
    
    log_action "=== MacFleet Code Signature Management Started ==="
    log_action "Action: $action"
    
    case "$action" in
        "analyze")
            if [[ -z "$param1" ]]; then
                echo "Usage: $0 analyze <app_path> [output_format] [security_check]"
                echo "Output formats: detailed, summary, json, csv"
                exit 1
            fi
            analyze_code_signature "$param1" "${param2:-detailed}" "${param3:-true}"
            ;;
        "extract")
            if [[ -z "$param1" ]]; then
                echo "Usage: $0 extract <app_path>"
                exit 1
            fi
            extract_code_requirement "$param1"
            ;;
        "batch")
            if [[ -z "$param1" ]]; then
                param1="/Applications"
            fi
            batch_analyze_applications "$param1" "${param2:-table}" "${param3:-false}"
            ;;
        "policy")
            if [[ -z "$param1" ]]; then
                echo "Available policies: ${!SIGNING_POLICIES[*]}"
                exit 1
            fi
            enforce_code_signing_policy "$param1" "${param2:-report}" "${param3:-/Applications}"
            ;;
        "fleet")
            local paths=("${@:2}")
            if [[ ${#paths[@]} -eq 0 ]]; then
                paths=("/Applications" "/System/Applications")
            fi
            analyze_fleet_applications "${paths[@]}"
            ;;
        "verify")
            if [[ -z "$param1" ]]; then
                echo "Usage: $0 verify <app_path>"
                exit 1
            fi
            if codesign -v "$param1" 2>/dev/null; then
                echo "✓ Code signature is valid for: $param1"
            else
                echo "⚠️ Code signature verification failed for: $param1"
                codesign -v "$param1"
            fi
            ;;
        "help")
            echo "Usage: $0 [action] [options...]"
            echo "Actions:"
            echo "  analyze <app_path> [format] [security] - Analyze application signature"
            echo "  extract <app_path> - Extract code requirement only"
            echo "  batch <scan_path> [format] [include_system] - Batch analyze applications"
            echo "  policy <policy_name> [action] [path] - Enforce signing policy"
            echo "  fleet [paths...] - Analyze fleet-wide applications"
            echo "  verify <app_path> - Quick signature verification"
            echo "  help - Show this help"
            echo ""
            echo "Output Formats: detailed, summary, json, csv, table"
            echo "Policies: ${!SIGNING_POLICIES[*]}"
            echo "Security Levels: ${!SECURITY_LEVELS[*]}"
            ;;
        *)
            log_action "ERROR: Unknown action: $action"
            echo "Use '$0 help' for usage information"
            exit 1
            ;;
    esac
    
    log_action "=== Code signature management completed ==="
}

# Execute main function
main "$@"

Security Best Practices

Enterprise Certificate Management

#!/bin/bash

# Enterprise certificate validation
validate_enterprise_certificates() {
    local cert_policy="${1:-strict}"
    
    echo "=== Enterprise Certificate Validation ==="
    echo "Policy: $cert_policy"
    echo ""
    
    local expired_certs=()
    local revoked_certs=()
    local untrusted_certs=()
    
    # Check system keychain for enterprise certificates
    security find-certificate -a -p /Library/Keychains/System.keychain | \
    while IFS= read -r cert_line; do
        if [[ "$cert_line" == "-----BEGIN CERTIFICATE-----" ]]; then
            # Process certificate
            echo "Checking enterprise certificate..."
        fi
    done
    
    echo "✓ Enterprise certificate validation completed"
}

validate_enterprise_certificates "strict"

Compliance Reporting

#!/bin/bash

# Generate compliance report for regulatory requirements
generate_compliance_report() {
    local compliance_type="${1:-general}"
    local output_file="$REPORT_DIR/compliance_${compliance_type}_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Generating Compliance Report: $compliance_type ==="
    
    local total_apps=0
    local compliant_apps=0
    local non_compliant_apps=0
    
    # Scan applications for compliance
    while IFS= read -r -d '' app_path; do
        ((total_apps++))
        
        case "$compliance_type" in
            "hipaa")
                check_hipaa_compliance "$app_path" compliant_apps non_compliant_apps
                ;;
            "sox")
                check_sox_compliance "$app_path" compliant_apps non_compliant_apps
                ;;
            "pci_dss")
                check_pci_compliance "$app_path" compliant_apps non_compliant_apps
                ;;
            *)
                check_general_compliance "$app_path" compliant_apps non_compliant_apps
                ;;
        esac
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    # Generate report
    local compliance_percentage=$(( compliant_apps * 100 / total_apps ))
    
    cat > "$output_file" << EOF
{
    "compliance_report": {
        "type": "$compliance_type",
        "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
        "hostname": "$(hostname)",
        "summary": {
            "total_applications": $total_apps,
            "compliant_applications": $compliant_apps,
            "non_compliant_applications": $non_compliant_apps,
            "compliance_percentage": $compliance_percentage
        }
    }
}
EOF
    
    echo "Compliance report generated: $output_file"
    echo "Compliance rate: $compliance_percentage%"
}

generate_compliance_report "general"

Important Notes

  • Code signatures verify application authenticity and integrity
  • Code requirements specify constraints for application validation
  • Enterprise policies should balance security with operational needs
  • Regular monitoring helps detect unauthorized applications
  • Compliance requirements vary by industry and regulatory framework
  • Notarization provides additional security validation from Apple
  • Certificate management is crucial for enterprise security
  • Fleet-wide analysis enables proactive security management

Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Configurando un Runner de GitHub Actions en un Mac Mini (Apple Silicon)

Runner de GitHub Actions

GitHub Actions es una plataforma poderosa de CI/CD que te permite automatizar tus flujos de trabajo de desarrollo de software. Aunque GitHub ofrece runners hospedados, los runners auto-hospedados proporcionan mayor control y personalización para tu configuración de CI/CD. Este tutorial te guía a través de la configuración y conexión de un runner auto-hospedado en un Mac mini para ejecutar pipelines de macOS.

Prerrequisitos

Antes de comenzar, asegúrate de tener:

  • Un Mac mini (regístrate en Macfleet)
  • Un repositorio de GitHub con derechos de administrador
  • Un gestor de paquetes instalado (preferiblemente Homebrew)
  • Git instalado en tu sistema

Paso 1: Crear una Cuenta de Usuario Dedicada

Primero, crea una cuenta de usuario dedicada para el runner de GitHub Actions:

# Crear la cuenta de usuario 'gh-runner'
sudo dscl . -create /Users/gh-runner
sudo dscl . -create /Users/gh-runner UserShell /bin/bash
sudo dscl . -create /Users/gh-runner RealName "GitHub runner"
sudo dscl . -create /Users/gh-runner UniqueID "1001"
sudo dscl . -create /Users/gh-runner PrimaryGroupID 20
sudo dscl . -create /Users/gh-runner NFSHomeDirectory /Users/gh-runner

# Establecer la contraseña para el usuario
sudo dscl . -passwd /Users/gh-runner tu_contraseña

# Agregar 'gh-runner' al grupo 'admin'
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Cambia a la nueva cuenta de usuario:

su gh-runner

Paso 2: Instalar Software Requerido

Instala Git y Rosetta 2 (si usas Apple Silicon):

# Instalar Git si no está ya instalado
brew install git

# Instalar Rosetta 2 para Macs Apple Silicon
softwareupdate --install-rosetta

Paso 3: Configurar el Runner de GitHub Actions

  1. Ve a tu repositorio de GitHub
  2. Navega a Configuración > Actions > Runners

Runner de GitHub Actions

  1. Haz clic en "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Selecciona macOS como imagen del runner y ARM64 como arquitectura
  3. Sigue los comandos proporcionados para descargar y configurar el runner

Runner de GitHub Actions

Crea un archivo .env en el directorio _work del runner:

# archivo _work/.env
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Ejecuta el script run.sh en tu directorio del runner para completar la configuración.
  2. Verifica que el runner esté activo y escuchando trabajos en la terminal y revisa la configuración del repositorio de GitHub para la asociación del runner y el estado Idle.

Runner de GitHub Actions

Paso 4: Configurar Sudoers (Opcional)

Si tus acciones requieren privilegios de root, configura el archivo sudoers:

sudo visudo

Agrega la siguiente línea:

gh-runner ALL=(ALL) NOPASSWD: ALL

Paso 5: Usar el Runner en Flujos de Trabajo

Configura tu flujo de trabajo de GitHub Actions para usar el runner auto-hospedado:

name: Flujo de trabajo de muestra

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - name: Instalar NodeJS
        run: brew install node

El runner está autenticado en tu repositorio y etiquetado con self-hosted, macOS, y ARM64. Úsalo en tus flujos de trabajo especificando estas etiquetas en el campo runs-on:

runs-on: [self-hosted, macOS, ARM64]

Mejores Prácticas

  • Mantén tu software del runner actualizado
  • Monitorea regularmente los logs del runner para problemas
  • Usa etiquetas específicas para diferentes tipos de runners
  • Implementa medidas de seguridad apropiadas
  • Considera usar múltiples runners para balanceo de carga

Solución de Problemas

Problemas comunes y soluciones:

  1. Runner no conectando:

    • Verifica conectividad de red
    • Verifica validez del token de GitHub
    • Asegúrate de permisos apropiados
  2. Fallas de construcción:

    • Verifica instalación de Xcode
    • Verifica dependencias requeridas
    • Revisa logs del flujo de trabajo
  3. Problemas de permisos:

    • Verifica permisos de usuario
    • Verifica configuración de sudoers
    • Revisa permisos del sistema de archivos

Conclusión

Ahora tienes un runner auto-hospedado de GitHub Actions configurado en tu Mac mini. Esta configuración te proporciona más control sobre tu entorno de CI/CD y te permite ejecutar flujos de trabajo específicos de macOS de manera eficiente.

Recuerda mantener regularmente tu runner y mantenerlo actualizado con los últimos parches de seguridad y versiones de software.

Aplicación Nativa

Aplicación nativa de Macfleet

Guía de Instalación de Macfleet

Macfleet es una solución poderosa de gestión de flota diseñada específicamente para entornos de Mac Mini alojados en la nube. Como proveedor de hosting en la nube de Mac Mini, puedes usar Macfleet para monitorear, gestionar y optimizar toda tu flota de instancias Mac virtualizadas.

Esta guía de instalación te llevará a través de la configuración del monitoreo de Macfleet en sistemas macOS, Windows y Linux para asegurar una supervisión integral de tu infraestructura en la nube.

🍎 macOS

  • Descarga el archivo .dmg para Mac aquí
  • Haz doble clic en el archivo .dmg descargado
  • Arrastra la aplicación Macfleet a la carpeta Aplicaciones
  • Expulsa el archivo .dmg
  • Abre Preferencias del Sistema > Seguridad y Privacidad
    • Pestaña Privacidad > Accesibilidad
    • Marca Macfleet para permitir el monitoreo
  • Inicia Macfleet desde Aplicaciones
  • El seguimiento comienza automáticamente

🪟 Windows

  • Descarga el archivo .exe para Windows aquí
  • Haz clic derecho en el archivo .exe > "Ejecutar como administrador"
  • Sigue el asistente de instalación
  • Acepta los términos y condiciones
  • Permite en Windows Defender si se solicita
  • Concede permisos de monitoreo de aplicaciones
  • Inicia Macfleet desde el Menú Inicio
  • La aplicación comienza el seguimiento automáticamente

🐧 Linux

  • Descarga el paquete .deb (Ubuntu/Debian) o .rpm (CentOS/RHEL) aquí
  • Instala usando tu gestor de paquetes
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Permite permisos de acceso X11 si se solicita
  • Agrega el usuario a los grupos apropiados si es necesario
  • Inicia Macfleet desde el menú de Aplicaciones
  • La aplicación comienza el seguimiento automáticamente

Nota: Después de la instalación en todos los sistemas, inicia sesión con tus credenciales de Macfleet para sincronizar datos con tu panel de control.