Tutorial

New updates and improvements to Roundfleet.

Important notice

The code examples and scripts provided in these tutorials are for educational purposes only. Roundfleet is not responsible for any issues, damages, or security vulnerabilities that may arise from the use, modification, or implementation of these examples. Always review and test code in a secure environment before using it in production systems.

Certificate Distribution and Management on macOS

Efficiently distribute and manage security certificates across your Roundfleet devices using automated command-line tools. This tutorial covers certificate deployment, validation, trust management, and enterprise CA integration.

Understanding macOS Certificate Management

macOS uses several certificate stores and trust policies:

  • System Keychain - System-wide trusted certificates
  • Login Keychain - User-specific certificates
  • Trust Settings - Certificate trust policies and validation rules
  • Certificate Authority - Root and intermediate CA certificates

Basic Certificate Distribution

Download and Install Certificate

#!/bin/bash

# Basic certificate installation from URL
CERT_URL="https://your-domain.com/certificates/company-ca.crt"
CERT_NAME="company-ca.crt"

# Download certificate
curl -O "$CERT_URL"

# Add to system keychain with trust
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "/Users/$CERT_NAME"

echo "Certificate installed successfully"

Install Local Certificate

#!/bin/bash

# Install certificate from local file
CERT_PATH="/path/to/certificate.crt"

if [[ -f "$CERT_PATH" ]]; then
    sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$CERT_PATH"
    echo "Local certificate installed successfully"
else
    echo "Certificate file not found: $CERT_PATH"
    exit 1
fi

Enterprise Certificate Management Script

#!/bin/bash

# Roundfleet Certificate Distribution and Management
# Deploy, validate, and manage certificates across enterprise devices

# Configuration
LOG_FILE="/var/log/roundfleet_certificates.log"
BACKUP_DIR="/var/backups/certificates"
CERT_STORE_DIR="/Library/Keychains"
TEMP_DIR="/tmp/roundfleet_certs"

# Certificate sources configuration
declare -A CERTIFICATE_SOURCES=(
    ["company-root-ca"]="https://pki.company.com/certs/root-ca.crt"
    ["company-intermediate-ca"]="https://pki.company.com/certs/intermediate-ca.crt"
    ["company-ssl-ca"]="https://pki.company.com/certs/ssl-ca.crt"
)

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

# Create necessary directories
setup_directories() {
    for dir in "$BACKUP_DIR" "$TEMP_DIR"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
            log_action "Created directory: $dir"
        fi
    done
}

# Validate certificate file
validate_certificate() {
    local cert_file="$1"
    local cert_name="$2"
    
    log_action "Validating certificate: $cert_name"
    
    # Check if file exists and is readable
    if [[ ! -f "$cert_file" || ! -r "$cert_file" ]]; then
        log_action "ERROR: Certificate file not accessible: $cert_file"
        return 1
    fi
    
    # Validate certificate format using openssl
    if ! openssl x509 -in "$cert_file" -text -noout >/dev/null 2>&1; then
        log_action "ERROR: Invalid certificate format: $cert_name"
        return 1
    fi
    
    # Extract certificate information
    local subject
    local issuer
    local expiry
    subject=$(openssl x509 -in "$cert_file" -subject -noout | sed 's/subject=//')
    issuer=$(openssl x509 -in "$cert_file" -issuer -noout | sed 's/issuer=//')
    expiry=$(openssl x509 -in "$cert_file" -enddate -noout | sed 's/notAfter=//')
    
    log_action "Certificate Details:"
    log_action "  Subject: $subject"
    log_action "  Issuer: $issuer"
    log_action "  Expires: $expiry"
    
    # Check if certificate is expired
    if ! openssl x509 -in "$cert_file" -checkend 0 >/dev/null 2>&1; then
        log_action "WARNING: Certificate is expired: $cert_name"
        return 2
    fi
    
    # Check if certificate expires within 30 days
    if ! openssl x509 -in "$cert_file" -checkend 2592000 >/dev/null 2>&1; then
        log_action "WARNING: Certificate expires within 30 days: $cert_name"
    fi
    
    log_action "Certificate validation successful: $cert_name"
    return 0
}

# Download certificate from URL
download_certificate() {
    local cert_name="$1"
    local cert_url="$2"
    local cert_file="$TEMP_DIR/$cert_name.crt"
    
    log_action "Downloading certificate: $cert_name from $cert_url"
    
    # Download with validation
    if curl -s -f -L --connect-timeout 30 --max-time 120 -o "$cert_file" "$cert_url"; then
        log_action "Certificate downloaded successfully: $cert_name"
        echo "$cert_file"
        return 0
    else
        log_action "ERROR: Failed to download certificate: $cert_name"
        return 1
    fi
}

# Backup existing certificates
backup_certificates() {
    local backup_timestamp
    backup_timestamp=$(date '+%Y%m%d_%H%M%S')
    local backup_file="$BACKUP_DIR/keychain_backup_$backup_timestamp.tar.gz"
    
    log_action "Creating certificate backup: $backup_file"
    
    if tar -czf "$backup_file" -C /Library/Keychains . 2>/dev/null; then
        log_action "Backup created successfully: $backup_file"
        return 0
    else
        log_action "ERROR: Failed to create backup"
        return 1
    fi
}

# Install certificate to system keychain
install_certificate() {
    local cert_file="$1"
    local cert_name="$2"
    local trust_policy="${3:-trustRoot}"
    
    log_action "Installing certificate: $cert_name with trust policy: $trust_policy"
    
    # Check if certificate already exists
    if security find-certificate -c "$cert_name" /Library/Keychains/System.keychain >/dev/null 2>&1; then
        log_action "Certificate already exists in system keychain: $cert_name"
        
        # Optionally remove existing certificate
        read -p "Certificate already exists. Replace? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            remove_certificate "$cert_name"
        else
            return 0
        fi
    fi
    
    # Install certificate
    if sudo security add-trusted-cert -d -r "$trust_policy" -k /Library/Keychains/System.keychain "$cert_file"; then
        log_action "Certificate installed successfully: $cert_name"
        
        # Verify installation
        if security find-certificate -c "$cert_name" /Library/Keychains/System.keychain >/dev/null 2>&1; then
            log_action "Certificate verification successful: $cert_name"
            return 0
        else
            log_action "ERROR: Certificate verification failed: $cert_name"
            return 1
        fi
    else
        log_action "ERROR: Failed to install certificate: $cert_name"
        return 1
    fi
}

# Remove certificate from system keychain
remove_certificate() {
    local cert_name="$1"
    
    log_action "Removing certificate: $cert_name"
    
    if sudo security delete-certificate -c "$cert_name" /Library/Keychains/System.keychain 2>/dev/null; then
        log_action "Certificate removed successfully: $cert_name"
        return 0
    else
        log_action "Certificate not found or removal failed: $cert_name"
        return 1
    fi
}

# Configure authorization database
configure_authorization() {
    log_action "Configuring authorization database for certificate management"
    
    cat > "$TEMP_DIR/auth_config.plist" << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>allow-root</key>
    <true/>
    <key>authenticate-user</key>
    <true/>
    <key>class</key>
    <string>user</string>
    <key>comment</key>
    <string>Checked by the Admin framework when making changes to certain System Preferences.</string>
    <key>group</key>
    <string>admin</string>
    <key>session-owner</key>
    <false/>
    <key>shared</key>
    <true/>
</dict>
</plist>
EOF

    if sudo /usr/bin/security authorizationdb write system.preferences < "$TEMP_DIR/auth_config.plist"; then
        log_action "Authorization database configured successfully"
        return 0
    else
        log_action "ERROR: Failed to configure authorization database"
        return 1
    fi
}

# List installed certificates
list_certificates() {
    log_action "Listing installed certificates in system keychain"
    
    echo "=== System Keychain Certificates ==="
    security find-certificate -a -p /Library/Keychains/System.keychain | \
    openssl x509 -text -noout | \
    grep -E "(Subject:|Issuer:|Not After)" | \
    sed 's/^[[:space:]]*//'
    
    echo -e "\n=== Certificate Summary ==="
    local cert_count
    cert_count=$(security find-certificate -a /Library/Keychains/System.keychain | grep -c "keychain:")
    echo "Total certificates in system keychain: $cert_count"
}

# Generate certificate report
generate_certificate_report() {
    local report_file="$BACKUP_DIR/certificate_report_$(date '+%Y%m%d_%H%M%S').json"
    
    log_action "Generating certificate report: $report_file"
    
    {
        echo "{"
        echo "  \"report_date\": \"$(date -Iseconds)\","
        echo "  \"hostname\": \"$(hostname)\","
        echo "  \"system_info\": {"
        echo "    \"os_version\": \"$(sw_vers -productVersion)\","
        echo "    \"build_version\": \"$(sw_vers -buildVersion)\""
        echo "  },"
        echo "  \"certificates\": ["
        
        local first=true
        while IFS= read -r cert_hash; do
            if [[ "$first" == true ]]; then
                first=false
            else
                echo ","
            fi
            
            local cert_info
            cert_info=$(security find-certificate -a -Z /Library/Keychains/System.keychain | \
                       awk -v hash="$cert_hash" '
                       /^SHA-1 hash:/ { if ($3 == hash) found=1; next }
                       found && /^[[:space:]]*"/ { print; found=0 }')
            
            echo "    {"
            echo "      \"hash\": \"$cert_hash\","
            echo "      \"subject\": $cert_info"
            echo -n "    }"
        done < <(security find-certificate -a -Z /Library/Keychains/System.keychain | \
                 grep "SHA-1 hash:" | awk '{print $3}')
        
        echo ""
        echo "  ],"
        echo "  \"trust_settings\": $(security trust-settings-export -d /tmp/trust_settings.plist && plutil -convert json -o - /tmp/trust_settings.plist 2>/dev/null || echo 'null')"
        echo "}"
    } > "$report_file"
    
    log_action "Certificate report generated: $report_file"
    echo "$report_file"
}

# Deploy multiple certificates
deploy_certificate_bundle() {
    log_action "=== Starting certificate bundle deployment ==="
    
    local success_count=0
    local total_count=0
    local failed_certs=()
    
    for cert_name in "${!CERTIFICATE_SOURCES[@]}"; do
        total_count=$((total_count + 1))
        local cert_url="${CERTIFICATE_SOURCES[$cert_name]}"
        
        log_action "Processing certificate: $cert_name"
        
        # Download certificate
        local cert_file
        if cert_file=$(download_certificate "$cert_name" "$cert_url"); then
            
            # Validate certificate
            if validate_certificate "$cert_file" "$cert_name"; then
                
                # Install certificate
                if install_certificate "$cert_file" "$cert_name"; then
                    success_count=$((success_count + 1))
                    log_action "Successfully deployed certificate: $cert_name"
                else
                    failed_certs+=("$cert_name")
                fi
            else
                failed_certs+=("$cert_name")
            fi
            
            # Clean up temporary file
            rm -f "$cert_file"
        else
            failed_certs+=("$cert_name")
        fi
    done
    
    log_action "=== Certificate deployment summary ==="
    log_action "Total certificates: $total_count"
    log_action "Successfully deployed: $success_count"
    log_action "Failed deployments: ${#failed_certs[@]}"
    
    if [[ ${#failed_certs[@]} -gt 0 ]]; then
        log_action "Failed certificates: ${failed_certs[*]}"
    fi
    
    return $((total_count - success_count))
}

# Main execution function
main() {
    local action="${1:-deploy}"
    
    log_action "=== Roundfleet Certificate Management Started ==="
    log_action "Action: $action"
    log_action "Hostname: $(hostname)"
    log_action "User: $(whoami)"
    
    # Setup
    setup_directories
    
    case "$action" in
        "deploy")
            backup_certificates
            configure_authorization
            deploy_certificate_bundle
            list_certificates
            generate_certificate_report
            ;;
        "list")
            list_certificates
            ;;
        "report")
            generate_certificate_report
            ;;
        "backup")
            backup_certificates
            ;;
        "remove")
            if [[ -n "$2" ]]; then
                remove_certificate "$2"
            else
                echo "Usage: $0 remove <certificate_name>"
                exit 1
            fi
            ;;
        *)
            echo "Usage: $0 {deploy|list|report|backup|remove}"
            echo "  deploy  - Deploy all configured certificates"
            echo "  list    - List installed certificates"
            echo "  report  - Generate certificate report"
            echo "  backup  - Backup current certificates"
            echo "  remove  - Remove specific certificate"
            exit 1
            ;;
    esac
    
    # Cleanup
    rm -rf "$TEMP_DIR"
    
    log_action "=== Certificate management completed ==="
}

# Execute main function
main "$@"

Certificate Trust Policies

macOS supports different trust policies for certificates:

Trust PolicyDescriptionUse Case
trustRootTrusted root certificateCA root certificates
trustAsRootTrust as root without usage constraintsEnterprise CA certificates
denyExplicitly deny trustRevoked or blacklisted certificates
unspecifiedUse system default trustLet system determine trust

Certificate Validation and Security

Verify Certificate Chain

#!/bin/bash

# Verify certificate chain and validation
verify_certificate_chain() {
    local cert_file="$1"
    local ca_bundle="/etc/ssl/cert.pem"
    
    echo "Verifying certificate chain..."
    
    # Basic certificate verification
    if openssl verify -CAfile "$ca_bundle" "$cert_file"; then
        echo "✅ Certificate chain is valid"
    else
        echo "❌ Certificate chain verification failed"
        return 1
    fi
    
    # Check certificate purposes
    echo "Certificate purposes:"
    openssl x509 -in "$cert_file" -purpose -noout
    
    # Extract key usage
    echo "Key usage:"
    openssl x509 -in "$cert_file" -text -noout | grep -A 5 "X509v3 Key Usage"
    
    return 0
}

Certificate Security Scan

#!/bin/bash

# Security analysis of installed certificates
security_scan_certificates() {
    echo "=== Certificate Security Scan ==="
    
    # Find weak certificates (MD5 or SHA1 signatures)
    echo "Checking for weak signature algorithms..."
    security find-certificate -a -p /Library/Keychains/System.keychain | \
    while read -r cert_pem; do
        if [[ "$cert_pem" =~ ^-----BEGIN ]]; then
            local sig_algo
            sig_algo=$(echo "$cert_pem" | openssl x509 -text -noout | grep "Signature Algorithm" | head -1)
            
            if [[ "$sig_algo" =~ (md5|sha1) ]]; then
                local subject
                subject=$(echo "$cert_pem" | openssl x509 -subject -noout)
                echo "⚠️  Weak signature found: $subject - $sig_algo"
            fi
        fi
    done
    
    # Find expiring certificates
    echo -e "\nChecking for expiring certificates..."
    security find-certificate -a -p /Library/Keychains/System.keychain | \
    while read -r cert_pem; do
        if [[ "$cert_pem" =~ ^-----BEGIN ]]; then
            if ! echo "$cert_pem" | openssl x509 -checkend 2592000 >/dev/null 2>&1; then
                local subject
                local expiry
                subject=$(echo "$cert_pem" | openssl x509 -subject -noout)
                expiry=$(echo "$cert_pem" | openssl x509 -enddate -noout)
                echo "⚠️  Expiring certificate: $subject - $expiry"
            fi
        fi
    done
    
    echo "Security scan completed"
}

Automated Certificate Renewal

#!/bin/bash

# Automated certificate renewal script
setup_certificate_renewal() {
    local renewal_script="/usr/local/bin/roundfleet_cert_renewal.sh"
    local launchd_plist="/Library/LaunchDaemons/com.roundfleet.cert.renewal.plist"
    
    # Create renewal script
    cat > "$renewal_script" << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/roundfleet_cert_renewal.log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo "$(date): Starting automated certificate renewal"

# Add your certificate renewal logic here
# This could include:
# - Checking certificate expiration dates
# - Downloading new certificates from your PKI
# - Replacing expired certificates
# - Sending alerts about upcoming renewals

echo "$(date): Certificate renewal completed"
EOF

    chmod +x "$renewal_script"
    
    # Create LaunchDaemon for scheduled execution
    cat > "$launchd_plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.roundfleet.cert.renewal</string>
    <key>ProgramArguments</key>
    <array>
        <string>$renewal_script</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>2</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>
EOF

    # Load the LaunchDaemon
    sudo launchctl load "$launchd_plist"
    
    echo "Certificate renewal automation configured"
    echo "Script: $renewal_script"
    echo "Schedule: Daily at 2:00 AM"
}

Enterprise Integration

Active Directory Certificate Services

#!/bin/bash

# Integration with Microsoft ADCS
deploy_adcs_certificates() {
    local adcs_server="$1"
    local cert_template="$2"
    
    echo "Deploying certificates from ADCS: $adcs_server"
    
    # Request certificate from ADCS
    # This would typically use certreq or similar tools
    # Implementation depends on your ADCS configuration
    
    echo "ADCS integration requires additional configuration"
    echo "Please consult your PKI administrator"
}

SCEP (Simple Certificate Enrollment Protocol)

#!/bin/bash

# SCEP certificate enrollment
enroll_scep_certificate() {
    local scep_url="$1"
    local challenge_password="$2"
    
    echo "Enrolling certificate via SCEP: $scep_url"
    
    # Generate private key
    openssl genrsa -out device.key 2048
    
    # Create certificate request
    openssl req -new -key device.key -out device.csr -subj "/CN=$(hostname)"
    
    # Note: SCEP enrollment requires additional tools like sscep
    echo "SCEP enrollment requires sscep or similar tools"
    echo "Please install appropriate SCEP client"
}

Best Practices

🔐 Security Considerations

  • Validate certificates before installation
  • Use strong trust policies appropriate for certificate purpose
  • Monitor certificate expiration dates regularly
  • Implement certificate pinning for critical applications

📋 Management Guidelines

  • Backup certificates before making changes
  • Document certificate purposes and owners
  • Use automation for large-scale deployments
  • Monitor certificate usage and validity

🔍 Troubleshooting

  • Check certificate format if installation fails
  • Verify network connectivity for downloads
  • Review trust policies if certificates aren't trusted
  • Check system logs for detailed error information

Important Notes

  • Administrative privileges required for system keychain modifications
  • Certificate validation essential for security compliance
  • Backup existing certificates before bulk operations
  • Test certificate deployment on subset of devices first
  • Monitor certificate expiration to prevent service disruptions

Related Tutorials

Secure Token Management on macOS

Learn how to manage and grant secure tokens to users on macOS devices using sysadminctl for enterprise security, FileVault management, and cryptographic operations authentication.

Jul 7, 2025

Secure Data Erasure on macOS

Learn how to securely erase free space and manage data privacy on macOS devices using advanced secure erasure tools with compliance monitoring, privacy protection, and fleet-wide data security capabilities.

Jul 7, 2025

Secure Archive Management on macOS

Learn how to create, manage, and secure archives on macOS devices using password-protected ZIP files with enterprise-grade security, batch operations, compliance tracking, and fleet-wide archive management capabilities.

Jul 7, 2025

Screen Lock Security on macOS

Configure and enforce screen lock password requirements, sleep security policies, and automated lock mechanisms on macOS devices using AppleScript automation and enterprise security frameworks.

Jul 7, 2025

Remote Management and Secure Access Control on macOS

Comprehensive remote management configuration, secure access control, multi-protocol support, and enterprise compliance frameworks for macOS devices with advanced security monitoring and automated policy enforcement.

Jul 7, 2025

Password Management on macOS

Manage user passwords, implement password policies, and control authentication mechanisms on macOS devices using dscl, sysadminctl, and enterprise password management frameworks for fleet security.

Jul 7, 2025

Check App Code Signature on macOS

Verify application code signatures on Mac devices to ensure authenticity and detect tampering using codesign commands.

Jul 7, 2025

Wi-Fi Network Management on macOS

Manage Wi-Fi networks, configure proxy settings, DNS servers, and implement enterprise network policies on macOS devices using advanced networksetup commands and centralized fleet management.

Jul 7, 2025