Tag: infosec

  • Community Projects: Part 2

    DiskEncrypter – a shared shell script helps the world

    A Community Code Evolution Story: From Simple Utility to Security Tool

    Introduction

    What started as a straightforward 515-line bash script in October 2022 has evolved into a, uh, slightly more complicated, 1,125-line “enterprise-grade” encryption enforcement system.

    This is the story of how a simple external drive encryption script was shared with the community and provided a starting point for others to create their own “enhanced” utility which can now be one piece of a security solution that protects users from data loss, encourages users to encrypt external drives, and provides a framework to enforce an encryption policy.

    This DiskEncrypter_Enhanced.sh script is also a journey that spans nine major versions, each addressing real-world problems discovered through testing and user feedback. Along the way, I learned valuable lessons about user experience, data safety, and the importance of preventing accidental data loss—especially when it comes to irreplaceable photos and videos on camera cards.

    Chapter 1: The Beginning – DiskEncrypter.sh v1.0 (October 2022)

    The Original Vision

    Created by Thijs Xhaflaire, the original `DiskEncrypter.sh`script had a clear mission: automatically detect unencrypted external drives and prompt users to encrypt them. It was designed for macOS enterprise environments where data security compliance required all removable media to be encrypted.

    What It Did Well

    The original script handled the basics competently:

    – Detected external APFS, HFS+, and ExFAT/FAT volumes

    – Prompted users with a swiftDialog interface

    – Encrypted APFS volumes directly

    – Converted HFS+ volumes to APFS before encryption

    – Erased and reformatted ExFAT/FAT volumes as encrypted APFS

    – Offered a “mount as read-only” option for users who didn’t want encryption

    The Hidden Limitations

    But as deployments grew, limitations became apparent:

    1. Single-Volume Processing**

    If you inserted a disk with three partitions, the script would only process the first one. The other two? Invisible to the system.

    “`bash

    Original approach

    if [[ $StorageType =~ “Apple_APFS” ]]; then

        # Process APFS

    elif [[ $StorageType =~ “Apple_HFS” ]]; then

        # Process HFS+

    elif [[ $StorageType =~ “Microsoft Basic Data” ]]; then

        # Process ExFAT

    fi

    Only ONE branch executed, then script exits!

    “`

    2. No Logging Infrastructure**

    Troubleshooting was a nightmare. Basic `echo` statements went nowhere when run as a LaunchDaemon.

    3. No Testing Capability

    Want to test the script? Hope you don’t mind encrypting real drives, because there was no dry-run mode.

    4. Generic Error Messages

    “FileVault is disabled on disk4s1” – Great, but which drive is that? What’s it called?

    5. The Re-Prompt Problem

    User mounts a drive as read-only. Five minutes later, they insert another drive. The script triggers again and asks about BOTH drives, including the one already mounted read-only. Every. Single. Time.

    Chapter 2: The First Major Overhaul – v2.0 Enhanced (December 2025)

    ### Modernization for macOS 15+ Sequoia

    When macOS 15 (Sequoia) shipped, several compatibility issues emerged. This sparked a complete reimagining of the script’s architecture.

    ### The Two-Phase Architecture

    The biggest change was moving from a linear “process-and-exit” model to a sophisticated two-phase system:

    **Phase 1: Discovery**

    “`bash

    # Scan ALL drives

    # Detect ALL partitions

    # Check ALL volume types independently

    # Build a queue of unencrypted volumes

    “`

    **Phase 2: Processing**

    “`bash

    # Process each queued volume sequentially

    # Track what was encrypted

    # Show comprehensive summary at end

    “`

    This solved the multi-volume problem instantly. Now a disk with three APFS volumes, two HFS+ partitions, and an ExFAT partition would have ALL of them discovered and processed.

    ### The Multi-Volume Victory

    Here’s a real example of the difference:

    **Original v1.0 (Single partition found):**

    “`

    Processing /dev/disk4

    Found: disk4s1 (APFS volume “Old”)

    Encrypted: disk4s1

    Exit.

    “`

    **Enhanced v2.0 (All partitions found):**

    “`

    Discovery Phase:

    – disk29s1 “Old” (APFS, unencrypted) → Added to queue

    – disk29s2 “New” (APFS, unencrypted) → Added to queue

    – disk29s3 “Lucky” (APFS, unencrypted) → Added to queue

    – disk28s3 “Untitled 2” (HFS+, unencrypted) → Added to queue

    Processing Phase:

    [1/4] Encrypting “Old” (disk29s1)… Done

    [2/4] Encrypting “New” (disk29s2)… Done

    [3/4] Encrypting “Lucky” (disk29s3)… Done

    [4/4] Converting and encrypting “Untitled 2” (disk28s3)… Done

    Summary: 4 volumes encrypted in this session

    “`

    Volume Names Everywhere

    Every dialog, every log message, every notification now showed friendly volume names alongside technical IDs:

    “`

    Before:”Processing disk4s1″

    After:”Processing ‘MyBackups’ (disk4s1)”

    “`

    Users could finally understand what was happening to which drive.

    The LaunchDaemon Dialog Fix

    A critical bug emerged: when run as a LaunchDaemon (root), swiftDialog couldn’t accept keyboard input. Password fields were useless. The fix used `launchctl asuser` to run dialogs in the user’s GUI session context:

    Now dialogs appeared correctly with proper focus and keyboard interaction.

    Chapter 3: Production Readiness – v2.1 (December 2025)

    ### The Logging Revolution

    v2.1 introduced a professional four-level logging system:

    | Level | Output | Use Case |

    |——-|——–|———-|

    | **0 – Minimal** | Errors only | Production (silent success) |

    | **1 – Normal** | Errors + key operations | Standard deployment |

    | **2 – Verbose** | + detailed progress | Troubleshooting |

    | **3 – Debug** | Everything | Development/diagnosis |

    **Dual-destination logging** sent output to both console and macOS unified logging:

    “`bash

    log_info() {

        if [[ $LOG_LEVEL -ge 1 ]]; then

            echo “[$(get_timestamp)] INFO: $*”

            logger -p user.info “DiskEncrypter [INFO]: $*”

        fi

    }

    “`

    Now you could watch logs in real-time:

    “`bash

    log stream –predicate ‘eventMessage CONTAINS[c] “DiskEncrypter”‘ –info

    “`

    ### Dry-Run Mode: Test Without Fear

    “`bash

    sudo ./DiskEncrypter_Enhanced.sh –dry-run –log-level 3

    “`

    This became invaluable for:

    – Testing in production environments

    – Training new IT staff

    – Validating configuration changes

    – Demonstrations

    Every disk operation was logged but not executed:

    “`

    [DRY RUN] Would execute: diskutil apfs encryptVolume disk4s1 -user disk

    INFO: DRY RUN: Encryption would start for disk4s1

    “`

    ### Command-Line Arguments

    For the first time, you could override plist settings from the command line:

    “`bash

    # Debug logging without changing plist

    ./DiskEncrypter_Enhanced.sh -l 3

    # Dry-run with verbose logging

    ./DiskEncrypter_Enhanced.sh –dry-run -l 2

    “`

    Priority: **Command-line → Plist → Default**

    Chapter 4: The Bug Hunt – v2.2 (December 2025)

    ### The Critical Read-Only Bug

    A critical bug was discovered: the script couldn’t detect read-only volumes, causing users to be re-prompted endlessly.

    **The Bug:**

    “`bash

    # Wrong field name!

    volumeMountInfo=$(diskutil info “$VolumeID” | grep “Read-Only Volume:”)

    if [[ “$volumeMountInfo” == “Yes” ]]; then

        # This NEVER executed!

    fi

    “`

    **Why It Failed:**

    “`bash

    $ diskutil info disk4s2 | grep “Read-Only Volume:”

    # (no output – field doesn’t exist!)

    $ diskutil info disk4s2 | grep “Volume Read-Only:”

       Volume Read-Only:          Yes (read-only mount flag set)

    # ^^^ The actual field name!

    “`

    **The Fix:**

    “`bash

    volumeMountInfo=$(diskutil info “$VolumeID” | grep “Volume Read-Only:”)

    if [[ “$volumeMountInfo” =~ ^Yes ]]; then

        log_info “Volume $VolumeID ($volumeName) is mounted read-only, skipping”

        continue

    fi

    “`

    This simple fix eliminated the annoying re-prompt problem.

    ### NTFS Support Added

    Windows NTFS volumes were now recognized and handled:

    “`bash

    if [[ $StorageInfo =~ “Microsoft Basic Data” ]] ||

       [[ $StorageInfo =~ “Windows_FAT” ]] ||

       [[ $StorageInfo =~ “DOS_FAT” ]] ||

       [[ $StorageInfo =~ “Windows_NTFS” ]]; then  # NEW!

    “`

    Chapter 5: The Safety Revolution – v2.3 (December 2025)

    ### The Security Gap

    A realization hit: unencrypted drives were mounted read/write while waiting for user decision. This created a window where data could accidentally be written to unencrypted media.

    ### Auto Read-Only Mounting

    v2.3’s solution was elegant: automatically mount ALL unencrypted volumes as read-only immediately upon detection, before showing any dialog.

    **New Function:**

    “`bash

    mountReadOnly() {

        local VolumeID=$1

        local volumeName=$2

        log_info “Auto-mounting volume as read-only: $VolumeID ($volumeName)”

        # Unmount first

        diskutil unmountDisk “$VolumeID” 2>/dev/null

        # Mount as read-only

        diskutil mount readOnly “$VolumeID”

        return 0

    }

    “`

    **Updated Workflow:**

    “`

    v2.2:

    1. Drive detected → Mounted read/write ⚠️

    2. Dialog shown

    3. If “Mount read-only” → Remount

    v2.3:

    1. Drive detected → Auto-mounted read-only ✅

    2. Dialog shown (drive already safe)

    3. If “Keep Read-Only” → Already done!

    “`

    ### Updated User Messages

    Dialogs now explicitly stated the protection status:

    “`

    This volume has been mounted as read-only for your protection.

    To write files, you must encrypt the disk.

    “`

    Button labels changed:

    – **Before:** “Mount as read-only”

    – **After:** “Keep Read-Only” (more accurate)

    ### Comprehensive User Documentation

    v2.3 introduced `USER_GUIDE.md`, a 22KB end-user safety manual with:

    – Clear explanation of the three button options

    – Decision guide chart by drive type

    – Step-by-step backup workflows

    – Critical warnings for ExFAT/FAT32 (data loss risk)

    – Password management best practices

    **Example Safe Workflow:**

    “`

    WRONG:

    ❌ Insert ExFAT drive with photos → Click “Encrypt” → ALL PHOTOS DELETED

    CORRECT:

    ✅ Click “Keep Read-Only”

    ✅ Copy all photos to Mac

    ✅ Verify photos copied correctly

    ✅ Eject drive

    ✅ Re-insert drive

    ✅ NOW click “Encrypt”

    ✅ Photos safe on Mac, drive encrypted

    “`

    Chapter 6: The Camera Card Protection – v2.4 (December 2025)

    ### The Data Loss Catastrophe Waiting to Happen

    v2.3 was safer, but a critical vulnerability remained: users could still choose to “Erase and Encrypt” ExFAT/FAT32 drives, which contained camera cards with irreplaceable wedding photos, vacation videos, and professional photography.

    **The Risk Scenario:**

    “`

    1. Wedding photographer inserts SD card (ExFAT) with 500 photos

    2. Dialog appears: “Erase and Encrypt” option available

    3. Photographer thinks “encrypt” = “protect my photos”

    4. Clicks button, enters password

    5. ❌ ALL 500 WEDDING PHOTOS DELETED FOREVER

    “`

    This was a bad situation waiting to happen.

    ### The Bold Decision: Remove the Erase Option

    v2.4 made a controversial choice: **completely remove the encrypt option for ExFAT/FAT/NTFS volumes.**

    This volume cannot be encrypted without erasing all data.

    To protect your data from accidental loss, encryption is not

    offered for this disk type (ExFAT/FAT/NTFS).

    Why encryption is not offered:

    • Encrypting this volume type requires complete erasure

    • All existing data would be permanently lost

    • This protection prevents accidental data loss on camera cards,

      USB drives, and other portable media

    To encrypt this drive:

    1. Back up all data to a secure location

    2. Use Disk Utility to erase and format as APFS

    3. Then encryption can be applied without data loss”

    <

    Chapter 7: The UX Polish – v2.4.1 (December 2025)

    ### The Scrolling Problem

    v2.4’s dialogs had too much text—users had to scroll to see everything. This defeated the purpose of clear communication.

    **The Problem:**

    “`

    Main Dialog:

    [15+ lines of text explaining everything]

            ▼ SCROLL REQUIRED ▼

    “`

    ### The Infobox Solution

    v2.4.1 leveraged swiftDialog’s `–infobox` parameter to split content:

    “`bash

    # Concise main message (5 lines)

    customMessage=”Non-encryptable volume: **\”$volumeName\”** ($VolumeID)

    File System: **$fsType**

    $subTitleNonEncryptable”

    # Detailed infobox (collapsible)

    infoboxMessage=”### Why Encryption Is Not Offered

    • Encrypting this volume type requires **complete erasure**

    • All existing data would be **permanently lost**

    • This protection prevents accidental data loss

    ### To Encrypt This Drive (If Needed)

    **Step 1:** Back up all data to a secure location

    **Step 2:** Open Disk Utility and erase the drive

    **Step 3:** Format as **APFS** (Mac only)

    **Step 4:** Re-insert for automatic encryption

    ⚠️ **Warning:** Only proceed if you have backed up all data!”

    Chapter 8: The Feedback Loop Fix – v2.4.3

    ### The Duplicate Dialog Bug

    Just when everything seemed perfect, a new bug emerged in production:

    **The Problem:**

    “`

    1. User inserts USB drive

    2. Script displays dialog

    3. User clicks “Eject”

    4. Script executes: diskutil unmountDisk disk4

    5. ❌ Unmount event triggers LaunchDaemon AGAIN

    6. ❌ Script runs concurrently with first instance

    7. ❌ Dialog appears TWICE

    8. ❌ User must click “Eject” again

    “`

    The LaunchDaemon was monitoring both mount AND unmount events, creating a feedback loop.

    The Two-Pronged Solution

    v2.4.3 implemented two complementary mechanisms:

    **1. Lock File Mechanism (Prevents concurrent execution)**

    “`bash

    LOCK_FILE=”/var/run/diskencrypter.lock”

    **2. Processed Volumes Tracking (Prevents re-processing)**

    “`bash

    PROCESSED_VOLUMES_FILE=”/var/tmp/diskencrypter_processed.txt”

    COOLDOWN_SECONDS=30

    Chapter 9: The Spaces In Between – v2.4.5

    A few more big fixes happened in v2.4.4 and v2.4.5 which addresses spaces not allowed in the password field and the password hint field and suffice it to say that regex was the issue. And testing was required. And a second fix for the first fix when the installer was discovered to be setting an old default in the management preferences. Ooops!

    Chapter 10: Lessons Learned

    ### 1. Users Don’t Understand Technical Terms

    “Erase and encrypt” sounds like “protect my data” to non-technical users. The v2.4 solution of removing the option entirely was controversial but necessary.

    **Key Learning:** When data loss is possible, don’t rely on warnings—remove the dangerous option.

    ### 2. Edge Cases Are Real Cases

    The read-only field name bug (v2.2) seemed minor until users reported constant re-prompts. The duplicate dialog bug (v2.4.3) only appeared in production when LaunchDaemon unmount events triggered re-execution.

    **Key Learning:** Real-world testing reveals issues that synthetic tests miss.

    ### 3. Good UX Is Iterative

    v2.4’s dialogs had too much text. v2.4.1’s infobox solution reduced reading time by 66%. Sometimes the best improvement is reduction, not addition.

    **Key Learning:** Watch real users interact with your interface. Simplify ruthlessly.

    ### 4. Logging Saves Lives (and Debugging Time)

    The v2.1 four-level logging system with dual output (console + system logger) paid dividends in every subsequent version. Debug output that would have taken hours to add per-bug was already there.

    **Key Learning:** Invest in logging infrastructure early. Future you will thank present you.

    ### 5. Backward Compatibility Matters

    Every version maintained 100% backward compatibility with configuration files and user workflows. APFS/HFS+ encryption worked identically across all versions.

    **Key Learning:** Add features, don’t break existing deployments.

    ### 6. Concurrent Execution Is Harder Than It Looks

    The feedback loop bug (v2.4.3) required two complementary solutions: lock files for mutual exclusion AND processed volume tracking for cooldowns.

    **Key Learning:** Concurrent execution requires multiple layers of protection.

    Chapter 11: The Final Product

    ### What Disk Encrypter Enhanced v2.4.5 Delivers

    **For IT Administrators:**

    – Comprehensive logging with 4 verbosity levels

    – Dry-run mode for safe testing

    – Command-line arguments for flexibility

    – Automatic log rotation (30-day retention)

    – Lock file protection against race conditions

    – Processed volume tracking with configurable cooldown

    **For End Users:**

    – Auto read-only mounting (immediate protection)

    – Clear, friendly volume names in all dialogs

    – No risk of accidental data loss on camera cards

    – Clean, professional dialog layout (no scrolling)

    – One dialog per user decision (no duplicates)

    – Educational content explaining technical concepts

    **For Compliance Officers:**

    – Complete audit trail in system logs

    – Encryption enforcement for compatible volumes

    – Safe handling of non-compatible media

    – User acknowledgment tracking

    – Session-based encryption reporting

    Chapter 12: Looking Forward

    ### What’s Next?

    The evolution from v1.0 to v2.4.5 represents maturity, but there’s always room for improvement:

    **Potential Future Enhancements:**

    – Web-based dashboard for monitoring encryption across enterprise

    – Centralized reporting to MDM systems

    – Email notifications for IT admins

    – Custom encryption policies per volume type

    – Integration with company password managers

    – Support for FileVault-encrypted APFS containers on external drives

    ### The Philosophy Going Forward

    The journey from v1.0 to v2.4.5 taught us three guiding principles:

    1. **User safety over feature completeness** – Removing the ExFAT erase option protected users from themselves

    2. **Comprehensive logging over simplicity** – The debugging investment paid dividends

    3. **Iterative refinement over big-bang releases** – Each version solved real problems discovered in the field

    ## Conclusion

    What started as a 515-line utility script in October 2022 has evolved into a 1,325-line enterprise-grade security solution. Along the way, we learned that good software is never “done”—it evolves through real-world use, user feedback, and a commitment to continuous improvement.

    The DiskEncrypter journey demonstrates that the best solutions emerge from:

    – Listening to users (the read-only re-prompt problem)

    – Protecting users from themselves (the camera card protection)

    – Obsessive attention to detail (the dialog UX refinement)

    – Comprehensive testing (the feedback loop bug discovery)

    Today, DiskEncrypter_Enhanced.sh v2.4.5 stands as a cautionary tale to what iterative refinement can achieve: a tool that not only enforces security policy but actively prevents data loss, provides excellent user experience, and operates reliably in production environments.

    The code journey continues.

    **Technical Stats:**

    – **Total Development Time:** ~12 days of Xmas coding (December 3-15, 2025)

    – **Lines of Code:** 515 → 1,125 (+157%)

    – **Functions:** 2 → 25+ (+1,150%)

    – **Major Versions:** 9 (v2.0, v2.1, v2.2, v2.3, v2.4, v2.4.1, v2.4.3, v2.4.4, v2.4.5)

    – **Bug Fixes:** 4 critical issues resolved

    – **Compatibility:** macOS 15+ (Sequoia) and macOS 26+

    **Documentation:**

    – Evolution Guide: 1,844 lines

    – User Guide: 650+ lines

    **Acknowledgments:**

    – Original script by Thijs Xhaflaire (October 2022)

    swiftDialog by Bart Reardon

    – Testing and feedback from the MacAdmin community

    **License:**

    The DiskEncrypter_Enhanced.sh script is distributed for enterprise use. See individual script files for license details.