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.

File Protection on macOS

Protect critical files and applications across your MacFleet devices using advanced file protection techniques, immutable flags, and enterprise security policies. This tutorial provides comprehensive tools for implementing organizational file security standards.

Understanding macOS File Protection Methods

macOS offers several file protection mechanisms:

  • Immutable Flags - System-level protection preventing modification or deletion
  • File Permissions - User and group-based access controls
  • Extended Attributes - Additional metadata for security enforcement
  • System Integrity Protection (SIP) - Built-in protection for system files

Basic File Protection Commands

Lock Single File

#!/bin/bash

# Lock a file using immutable flag
FILE_PATH="/Applications/WhatsApp.app"

chflags schg "$FILE_PATH"
echo "File locked: $FILE_PATH"

Unlock Single File

#!/bin/bash

# Unlock a file by removing immutable flag
FILE_PATH="/Applications/WhatsApp.app"

chflags noschg "$FILE_PATH"
echo "File unlocked: $FILE_PATH"

Check File Protection Status

#!/bin/bash

# Check if file is protected
FILE_PATH="/Applications/WhatsApp.app"

if ls -lO "$FILE_PATH" | grep -q "schg"; then
    echo "File is protected: $FILE_PATH"
else
    echo "File is not protected: $FILE_PATH"
fi

Enterprise File Protection System

#!/bin/bash

# MacFleet Enterprise File Protection System
# Comprehensive file security and protection management

# Configuration
MACFLEET_DIR="/etc/macfleet"
POLICIES_DIR="$MACFLEET_DIR/file_policies"
REPORTS_DIR="$MACFLEET_DIR/reports"
COMPLIANCE_DIR="$MACFLEET_DIR/compliance"
AUDIT_DIR="$MACFLEET_DIR/audit"
LOG_FILE="/var/log/macfleet_file_protection.log"
BACKUP_DIR="$MACFLEET_DIR/backups"

# Create directory structure
create_directories() {
    local dirs=("$MACFLEET_DIR" "$POLICIES_DIR" "$REPORTS_DIR" "$COMPLIANCE_DIR" "$AUDIT_DIR" "$BACKUP_DIR")
    for dir in "${dirs[@]}"; do
        [[ ! -d "$dir" ]] && mkdir -p "$dir"
    done
}

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

# File Categories for Enterprise Protection
declare -A FILE_CATEGORIES=(
    ["critical_apps"]="/Applications/System Preferences.app,/Applications/Terminal.app,/Applications/Utilities"
    ["business_apps"]="/Applications/Microsoft Office,/Applications/Slack.app,/Applications/Zoom.app"
    ["system_configs"]="/etc/hosts,/etc/sudoers,/etc/ssh/sshd_config"
    ["security_tools"]="/Applications/1Password.app,/Applications/Keychain Access.app"
    ["development_tools"]="/Applications/Xcode.app,/Applications/Visual Studio Code.app"
    ["admin_utilities"]="/Applications/Activity Monitor.app,/Applications/Console.app"
)

# Protection Levels
declare -A PROTECTION_LEVELS=(
    ["maximum"]="schg,uappnd,uchg"        # System immutable, user append-only, user immutable
    ["high"]="schg,uchg"                  # System immutable, user immutable
    ["moderate"]="uchg"                   # User immutable only
    ["minimal"]="uappnd"                  # User append-only
    ["readonly"]="ro"                     # Read-only access
)

# Security Policies
declare -A SECURITY_POLICIES=(
    ["enterprise_lockdown"]="critical_apps:maximum,business_apps:high,system_configs:maximum,security_tools:high"
    ["standard_protection"]="critical_apps:high,system_configs:high,security_tools:moderate"
    ["development_safe"]="critical_apps:moderate,system_configs:high,development_tools:minimal"
    ["minimal_security"]="system_configs:moderate,security_tools:minimal"
    ["maintenance_mode"]="system_configs:readonly"
)

# Apply protection to file or directory
protect_file() {
    local file_path="$1"
    local protection_level="$2"
    local policy="$3"
    
    if [[ ! -e "$file_path" ]]; then
        log_action "ERROR: File not found: $file_path"
        return 1
    fi
    
    # Backup current flags
    local current_flags=$(ls -lO "$file_path" 2>/dev/null | awk '{print $5}' || echo "none")
    echo "$file_path:$current_flags:$(date)" >> "$BACKUP_DIR/file_flags_backup.log"
    
    # Apply protection based on level
    local flags="${PROTECTION_LEVELS[$protection_level]}"
    if [[ -z "$flags" ]]; then
        log_action "ERROR: Unknown protection level: $protection_level"
        return 1
    fi
    
    # Split flags and apply each one
    IFS=',' read -ra flag_array <<< "$flags"
    for flag in "${flag_array[@]}"; do
        if chflags "$flag" "$file_path" 2>/dev/null; then
            log_action "Applied protection flag '$flag' to: $file_path (Policy: $policy)"
        else
            log_action "ERROR: Failed to apply flag '$flag' to: $file_path"
        fi
    done
    
    # Set restrictive permissions if needed
    if [[ "$protection_level" == "maximum" || "$protection_level" == "high" ]]; then
        chmod 644 "$file_path" 2>/dev/null
        log_action "Set restrictive permissions for: $file_path"
    fi
    
    # Save protection metadata
    echo "file=$file_path,protection=$protection_level,policy=$policy,timestamp=$(date),user=$(whoami)" >> "$POLICIES_DIR/applied_protections.log"
}

# Remove protection from file
unprotect_file() {
    local file_path="$1"
    local reason="$2"
    
    if [[ ! -e "$file_path" ]]; then
        log_action "ERROR: File not found: $file_path"
        return 1
    fi
    
    # Remove all common protection flags
    local flags_to_remove=("schg" "uchg" "uappnd" "ro")
    
    for flag in "${flags_to_remove[@]}"; do
        if chflags "no$flag" "$file_path" 2>/dev/null; then
            log_action "Removed protection flag '$flag' from: $file_path (Reason: $reason)"
        fi
    done
    
    # Restore standard permissions
    if [[ -f "$file_path" ]]; then
        chmod 644 "$file_path" 2>/dev/null
    elif [[ -d "$file_path" ]]; then
        chmod 755 "$file_path" 2>/dev/null
    fi
    
    log_action "File unprotected: $file_path (Reason: $reason)"
}

# Apply protection to file category
protect_category() {
    local category="$1"
    local protection_level="$2"
    local policy="$3"
    
    if [[ -z "${FILE_CATEGORIES[$category]}" ]]; then
        log_action "ERROR: Unknown file category: $category"
        return 1
    fi
    
    log_action "Protecting category: $category with level: $protection_level (Policy: $policy)"
    
    # Split comma-separated file paths
    IFS=',' read -ra files <<< "${FILE_CATEGORIES[$category]}"
    
    for file_path in "${files[@]}"; do
        # Handle wildcard patterns
        if [[ "$file_path" == *"*"* ]]; then
            # Use find for pattern matching
            find "${file_path%/*}" -name "${file_path##*/}" 2>/dev/null | while read -r found_file; do
                protect_file "$found_file" "$protection_level" "$policy"
            done
        else
            protect_file "$file_path" "$protection_level" "$policy"
        fi
    done
}

# Apply comprehensive security policy
apply_security_policy() {
    local policy="$1"
    
    if [[ -z "${SECURITY_POLICIES[$policy]}" ]]; then
        log_action "ERROR: Unknown security policy: $policy"
        return 1
    fi
    
    log_action "Applying security policy: $policy"
    
    # Parse policy string: "category:level,category:level"
    IFS=',' read -ra policy_items <<< "${SECURITY_POLICIES[$policy]}"
    
    for item in "${policy_items[@]}"; do
        IFS=':' read -ra parts <<< "$item"
        local category="${parts[0]}"
        local protection_level="${parts[1]}"
        
        protect_category "$category" "$protection_level" "$policy"
    done
    
    log_action "Security policy '$policy' applied successfully"
    
    # Generate compliance report
    generate_compliance_report "$policy"
}

# Bulk file protection operations
bulk_protect_files() {
    local file_list="$1"
    local protection_level="$2"
    local policy="$3"
    
    if [[ ! -f "$file_list" ]]; then
        log_action "ERROR: File list not found: $file_list"
        return 1
    fi
    
    log_action "Starting bulk protection operation from list: $file_list"
    local protected_count=0
    local failed_count=0
    
    while IFS= read -r file_path; do
        [[ -z "$file_path" || "$file_path" =~ ^#.*$ ]] && continue
        
        if protect_file "$file_path" "$protection_level" "$policy"; then
            ((protected_count++))
        else
            ((failed_count++))
        fi
    done < "$file_list"
    
    log_action "Bulk protection completed: $protected_count protected, $failed_count failed"
}

# Critical system file protection
protect_system_files() {
    local protection_mode="$1"
    
    log_action "Applying system file protection: $protection_mode"
    
    # Define critical system files
    local critical_files=(
        "/etc/hosts"
        "/etc/sudoers"
        "/etc/ssh/sshd_config"
        "/etc/passwd"
        "/etc/group"
        "/System/Library/LaunchDaemons"
        "/Library/LaunchDaemons"
        "/usr/bin/sudo"
        "/bin/sh"
        "/bin/bash"
    )
    
    case "$protection_mode" in
        "lockdown")
            for file in "${critical_files[@]}"; do
                if [[ -e "$file" ]]; then
                    protect_file "$file" "maximum" "system_lockdown"
                fi
            done
            ;;
        "standard")
            for file in "${critical_files[@]}"; do
                if [[ -e "$file" ]]; then
                    protect_file "$file" "high" "system_standard"
                fi
            done
            ;;
        "minimal")
            for file in "${critical_files[@]}"; do
                if [[ -e "$file" ]]; then
                    protect_file "$file" "moderate" "system_minimal"
                fi
            done
            ;;
    esac
    
    log_action "System file protection applied: $protection_mode"
}

# File integrity monitoring
monitor_file_integrity() {
    local monitoring_mode="$1"
    local monitor_file="$POLICIES_DIR/integrity_monitor.log"
    
    log_action "Starting file integrity monitoring: $monitoring_mode"
    
    # Get list of protected files
    local protected_files=()
    if [[ -f "$POLICIES_DIR/applied_protections.log" ]]; then
        while IFS=',' read -r entry; do
            if [[ "$entry" =~ file=([^,]+) ]]; then
                protected_files+=("${BASH_REMATCH[1]}")
            fi
        done < "$POLICIES_DIR/applied_protections.log"
    fi
    
    # Monitor files for changes
    for file in "${protected_files[@]}"; do
        if [[ -e "$file" ]]; then
            local current_hash=$(shasum -a 256 "$file" 2>/dev/null | cut -d' ' -f1)
            local current_flags=$(ls -lO "$file" 2>/dev/null | awk '{print $5}' || echo "none")
            local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
            
            echo "$timestamp:$file:$current_hash:$current_flags" >> "$monitor_file"
        fi
    done
    
    log_action "File integrity monitoring completed: $monitoring_mode"
}

# Emergency unlock procedures
emergency_unlock() {
    local reason="$1"
    local unlock_scope="$2"
    
    log_action "EMERGENCY UNLOCK INITIATED - Reason: $reason, Scope: $unlock_scope"
    
    case "$unlock_scope" in
        "all")
            # Unlock all protected files
            if [[ -f "$POLICIES_DIR/applied_protections.log" ]]; then
                while IFS=',' read -r entry; do
                    if [[ "$entry" =~ file=([^,]+) ]]; then
                        unprotect_file "${BASH_REMATCH[1]}" "emergency_unlock"
                    fi
                done < "$POLICIES_DIR/applied_protections.log"
            fi
            ;;
        "system")
            # Unlock only system files
            local system_files=("/etc/hosts" "/etc/sudoers" "/etc/ssh/sshd_config")
            for file in "${system_files[@]}"; do
                if [[ -e "$file" ]]; then
                    unprotect_file "$file" "emergency_system_unlock"
                fi
            done
            ;;
        "applications")
            # Unlock application files
            find /Applications -name "*.app" -exec chflags noschg {} \; 2>/dev/null
            log_action "Emergency unlock completed for applications"
            ;;
    esac
    
    # Create emergency unlock record
    echo "emergency_unlock:$reason:$unlock_scope:$(date):$(whoami)" >> "$AUDIT_DIR/emergency_operations.log"
    log_action "Emergency unlock completed: $unlock_scope"
}

# Generate comprehensive compliance report
generate_compliance_report() {
    local policy="$1"
    local report_file="$REPORTS_DIR/file_protection_compliance_$(date +%Y%m%d_%H%M%S).json"
    
    local protected_files_count=0
    local total_files_checked=0
    
    # Count protected files
    if [[ -f "$POLICIES_DIR/applied_protections.log" ]]; then
        protected_files_count=$(wc -l < "$POLICIES_DIR/applied_protections.log")
    fi
    
    # Count total files in categories
    for category in "${!FILE_CATEGORIES[@]}"; do
        IFS=',' read -ra files <<< "${FILE_CATEGORIES[$category]}"
        total_files_checked=$((total_files_checked + ${#files[@]}))
    done
    
    cat > "$report_file" << EOF
{
  "report_metadata": {
    "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
    "hostname": "$(hostname)",
    "policy_applied": "$policy",
    "report_version": "1.0"
  },
  "file_protection_status": {
    "protected_files_count": $protected_files_count,
    "total_files_checked": $total_files_checked,
    "protection_coverage": $(echo "scale=2; $protected_files_count * 100 / $total_files_checked" | bc -l 2>/dev/null || echo "0"),
    "emergency_unlocks_recorded": $([ -f "$AUDIT_DIR/emergency_operations.log" ] && wc -l < "$AUDIT_DIR/emergency_operations.log" || echo "0")
  },
  "security_policy": {
    "name": "$policy",
    "categories_protected": $(echo "${!FILE_CATEGORIES[@]}" | wc -w),
    "compliance_frameworks": ["SOX", "HIPAA", "NIST", "ISO27001", "PCI-DSS"]
  },
  "system_status": {
    "sip_enabled": $(csrutil status | grep -q "enabled" && echo "true" || echo "false"),
    "gatekeeper_enabled": $(spctl --status | grep -q "enabled" && echo "true" || echo "false"),
    "filevault_enabled": $(fdesetup status | grep -q "On" && echo "true" || echo "false"),
    "backup_available": $([ -f "$BACKUP_DIR/file_flags_backup.log" ] && echo "true" || echo "false")
  },
  "integrity_monitoring": {
    "active": $([ -f "$POLICIES_DIR/integrity_monitor.log" ] && echo "true" || echo "false"),
    "last_check": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
    "monitored_files": $([ -f "$POLICIES_DIR/integrity_monitor.log" ] && wc -l < "$POLICIES_DIR/integrity_monitor.log" || echo "0")
  }
}
EOF
    
    log_action "Compliance report generated: $report_file"
    echo "Report saved to: $report_file"
}

# Health check and validation
perform_health_check() {
    echo "=== MacFleet File Protection Health Check ==="
    
    # Check system integrity protection
    local sip_status=$(csrutil status 2>/dev/null || echo "Unknown")
    echo "✓ System Integrity Protection: $sip_status"
    
    # Check protected files
    local protected_count=0
    if [[ -f "$POLICIES_DIR/applied_protections.log" ]]; then
        protected_count=$(wc -l < "$POLICIES_DIR/applied_protections.log")
    fi
    echo "✓ Protected files: $protected_count"
    
    # Check critical system files
    local critical_protected=0
    local critical_files=("/etc/hosts" "/etc/sudoers" "/etc/ssh/sshd_config")
    for file in "${critical_files[@]}"; do
        if [[ -e "$file" ]] && ls -lO "$file" | grep -q "schg"; then
            ((critical_protected++))
        fi
    done
    echo "✓ Critical system files protected: $critical_protected/${#critical_files[@]}"
    
    # Check backup availability
    if [[ -f "$BACKUP_DIR/file_flags_backup.log" ]]; then
        local backup_entries=$(wc -l < "$BACKUP_DIR/file_flags_backup.log")
        echo "✓ Backup entries: $backup_entries"
    else
        echo "○ No backup records found"
    fi
    
    # Check integrity monitoring
    if [[ -f "$POLICIES_DIR/integrity_monitor.log" ]]; then
        local monitor_entries=$(wc -l < "$POLICIES_DIR/integrity_monitor.log")
        echo "✓ Integrity monitoring entries: $monitor_entries"
    else
        echo "○ No integrity monitoring active"
    fi
    
    # Check for emergency operations
    if [[ -f "$AUDIT_DIR/emergency_operations.log" ]]; then
        local emergency_count=$(wc -l < "$AUDIT_DIR/emergency_operations.log")
        echo "⚠️  Emergency operations recorded: $emergency_count"
    else
        echo "✓ No emergency operations recorded"
    fi
}

# Fleet deployment function
deploy_to_fleet() {
    local policy="$1"
    local fleet_file="$2"
    
    if [[ ! -f "$fleet_file" ]]; then
        log_action "ERROR: Fleet file not found: $fleet_file"
        return 1
    fi
    
    log_action "Starting fleet deployment of policy: $policy"
    
    while IFS= read -r host; do
        [[ -z "$host" || "$host" =~ ^#.*$ ]] && continue
        
        echo "Deploying to: $host"
        
        # Copy this script to remote host and execute
        ssh "$host" "bash -s" << EOF
#!/bin/bash
# Remote deployment of file protection policy: $policy

# Create directories
mkdir -p /etc/macfleet/{file_policies,reports,compliance,audit,backups}

# Apply the policy (simplified for remote execution)
$(declare -p FILE_CATEGORIES)
$(declare -p PROTECTION_LEVELS)
$(declare -p SECURITY_POLICIES)
$(type apply_security_policy | sed '1d')

apply_security_policy "$policy"
EOF
        
        if [[ $? -eq 0 ]]; then
            log_action "Successfully deployed to: $host"
        else
            log_action "Failed to deploy to: $host"
        fi
        
    done < "$fleet_file"
    
    log_action "Fleet deployment completed"
}

# Main execution function
main() {
    create_directories
    
    case "${1:-}" in
        "apply_policy")
            apply_security_policy "$2"
            ;;
        "protect_file")
            protect_file "$2" "${3:-moderate}" "manual"
            ;;
        "unprotect_file")
            unprotect_file "$2" "${3:-manual_unlock}"
            ;;
        "protect_category")
            protect_category "$2" "${3:-moderate}" "manual"
            ;;
        "bulk_protect")
            bulk_protect_files "$2" "${3:-moderate}" "bulk_operation"
            ;;
        "protect_system")
            protect_system_files "${2:-standard}"
            ;;
        "monitor_integrity")
            monitor_file_integrity "${2:-standard}"
            ;;
        "emergency_unlock")
            emergency_unlock "${2:-manual}" "${3:-all}"
            ;;
        "health_check")
            perform_health_check
            ;;
        "report")
            generate_compliance_report "${2:-manual}"
            ;;
        "deploy")
            deploy_to_fleet "$2" "$3"
            ;;
        "help"|*)
            echo "MacFleet Enterprise File Protection System"
            echo ""
            echo "Usage: $0 <command> [options]"
            echo ""
            echo "Commands:"
            echo "  apply_policy <policy>           - Apply security policy (enterprise_lockdown|standard_protection|development_safe|minimal_security|maintenance_mode)"
            echo "  protect_file <path> [level]     - Protect single file (maximum|high|moderate|minimal|readonly)"
            echo "  unprotect_file <path> [reason]  - Remove protection from file"
            echo "  protect_category <category> [level] - Protect file category (critical_apps|business_apps|system_configs|security_tools|development_tools|admin_utilities)"
            echo "  bulk_protect <file_list> [level] - Protect files from list"
            echo "  protect_system [mode]           - Protect system files (lockdown|standard|minimal)"
            echo "  monitor_integrity [mode]        - Monitor file integrity (standard|strict)"
            echo "  emergency_unlock <reason> [scope] - Emergency unlock (all|system|applications)"
            echo "  health_check                    - Perform system health check"
            echo "  report [policy]                 - Generate compliance report"
            echo "  deploy <policy> <fleet_file>    - Deploy policy to fleet"
            echo ""
            echo "Examples:"
            echo "  $0 apply_policy enterprise_lockdown"
            echo "  $0 protect_file /Applications/Terminal.app maximum"
            echo "  $0 protect_system lockdown"
            echo "  $0 emergency_unlock 'maintenance required' system"
            echo "  $0 health_check"
            ;;
    esac
}

# Execute main function
main "$@"

Protection Levels Explained

LevelDescriptionFlags AppliedUse Case
MaximumComplete system protectionschg, uappnd, uchgCritical system files
HighStrong user protectionschg, uchgImportant applications
ModerateStandard user protectionuchgBusiness applications
MinimalBasic append protectionuappndLog files, databases
Read-onlyPrevent modificationsroConfiguration templates

File Protection Use Cases

Protect Critical Applications

# Protect system utilities from modification
./file_protection.sh protect_category critical_apps maximum

# Protect business applications
./file_protection.sh protect_category business_apps high

System Configuration Protection

# Lock down system configuration files
./file_protection.sh protect_system lockdown

# Standard system protection
./file_protection.sh protect_system standard

Development Environment Safety

# Apply development-safe policy
./file_protection.sh apply_policy development_safe

# Protect development tools with minimal restrictions
./file_protection.sh protect_category development_tools minimal

Emergency Procedures

Complete System Unlock

# Emergency unlock all files
./file_protection.sh emergency_unlock "critical_maintenance" all

# Unlock only system files
./file_protection.sh emergency_unlock "system_update" system

File Recovery

# Check protection status
ls -lO /Applications/Terminal.app

# Remove specific protection
./file_protection.sh unprotect_file /Applications/Terminal.app "maintenance"

Important Security Considerations

  • System Integrity Protection (SIP) provides additional layer beyond file flags
  • Root privileges required for system immutable flags (schg)
  • Backup protection state before making changes
  • Emergency procedures should be tested and documented
  • File integrity monitoring helps detect unauthorized changes

Compliance and Auditing

The enterprise system provides comprehensive audit trails for:

  • SOX Compliance - Financial data protection requirements
  • HIPAA Requirements - Healthcare information security
  • PCI-DSS Standards - Payment card industry data security
  • NIST Framework - Cybersecurity standards compliance
  • ISO 27001 - Information security management

Testing and Validation

Before enterprise deployment:

  1. Test protection levels on non-critical files first
  2. Verify emergency unlock procedures work correctly
  3. Confirm business applications remain functional
  4. Test integrity monitoring accuracy
  5. Validate compliance reporting completeness

This comprehensive system transforms basic file locking into an enterprise-grade protection platform with advanced security policies, compliance monitoring, and fleet management capabilities.

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.