Skip to content

Automatic LUKS USB Decryption and Mounting

A Gentoo/OpenRC init.d script for unlocking a LUKS-encrypted external disk at boot and mounting it reliably using cryptsetup in non-interactive mode

Screenshot


Create /etc/conf.d/usbcrypt (set correct <password>)

# /etc/conf.d/usbcrypt

# LUKS container UUID (TYPE="crypto_LUKS")
USBCRYPT_DEV="/dev/disk/by-uuid/<uuid_to_luks_drive>"

# Mapping name: creates /dev/mapper/<name>
USBCRYPT_NAME="usb"

# Mountpoint after unlocking
USBCRYPT_MOUNTPOINT="/mnt/usb"

# FS type of the decrypted filesystem (optional but recommended)
USBCRYPT_FSTYPE="ext4"

# Mount options
USBCRYPT_MOUNTOPTS="defaults,noatime"

# Plaintext passphrase (chmod 600 this file)
USBCRYPT_PASSPHRASE="<password>"
EOF

Lock above down

chown root:root /etc/conf.d/usbcrypt
chmod 600 /etc/conf.d/usbcrypt

Configure /etc/init.d/autocrypt

cat << "EOF" > /etc/init.d/usbcrypt
#!/sbin/openrc-run
# /etc/init.d/usbcrypt

description="Unlock and mount a LUKS-encrypted external drive at boot"

depend() {
    need udev
    after modules
}

start() {
    local dev="${USBCRYPT_DEV}"
    local name="${USBCRYPT_NAME}"
    local mp="${USBCRYPT_MOUNTPOINT}"
    local mapper="/dev/mapper/${name}"

    # Ensure mountpoint exists
    checkpath -d -m 0755 "${mp}" || return 1

    # Wait up to ~30 seconds for the device to appear; resolve symlink robustly
    local i=0
    while :; do
        local realdev
        realdev="$(readlink -f "${dev}" 2>/dev/null)"

        if [ -n "${realdev}" ] && [ -b "${realdev}" ]; then
            dev="${realdev}"
            break
        fi

        if [ ${i} -ge 30 ]; then
            ewarn "Device ${USBCRYPT_DEV} not found; skipping."
            return 0
        fi

        sleep 1
        i=$((i+1))
    done

    # If already opened, skip opening (more reliable than checking -e /dev/mapper/*)
    if /sbin/cryptsetup status "${name}" >/dev/null 2>&1; then
        einfo "${mapper} already active; skipping luksOpen."
    else
        local pwlen="${#USBCRYPT_PASSPHRASE}"

        if [ "${pwlen}" -le 0 ]; then
            eerror "USBCRYPT_PASSPHRASE is empty."
            return 1
        fi

        ebegin "Decrypting ${USBCRYPT_DEV} (LUKS open as '${name}')"
        printf '%s' "${USBCRYPT_PASSPHRASE}" | \
            /sbin/cryptsetup --batch-mode --key-file=- --keyfile-size="${pwlen}" \
            luksOpen "${dev}" "${name}"
        rc=$?
        eend $rc || return 1
    fi

    # Mount if not mounted
    if mountpoint -q "${mp}"; then
        einfo "${mp} already mounted; nothing to do."
        return 0
    fi

    ebegin "Mounting decrypted drive into ${mp}"
    if [ -n "${USBCRYPT_FSTYPE}" ]; then
        mount -t "${USBCRYPT_FSTYPE}" -o "${USBCRYPT_MOUNTOPTS}" "${mapper}" "${mp}"
    else
        mount -o "${USBCRYPT_MOUNTOPTS}" "${mapper}" "${mp}"
    fi
    rc=$?
    eend $rc || return 1
}

stop() {
    local name="${USBCRYPT_NAME}"
    local mp="${USBCRYPT_MOUNTPOINT}"
    local mapper="/dev/mapper/${name}"

    if mountpoint -q "${mp}"; then
        ebegin "Unmounting ${mp}"
        umount "${mp}"
        rc=$?
        eend $rc || return 1
    else
        einfo "${mp} is not mounted."
    fi

    if /sbin/cryptsetup status "${name}" >/dev/null 2>&1; then
        ebegin "Closing LUKS mapping ${mapper}"
        /sbin/cryptsetup luksClose "${name}"
        rc=$?
        eend $rc || return 1
    else
        einfo "${mapper} is not active; nothing to close."
    fi
}
EOF 

Make it executable:

chmod +x /etc/init.d/usbcrypt

Enable on boot

rc-update add usbcrypt default

Start it now to test

rc-service usbcrypt start

Stop it now to test

rc-service usbcrypt stop