When managing multiple LUKS-encrypted volumes on headless servers, manually executing cryptsetup luksOpen
for each device after every reboot becomes tedious. The standard approach requires remembering UUID-device mappings:
# Typical manual process
cryptsetup luksOpen /dev/sdb1 db_encrypted
cryptsetup luksOpen /dev/nvme0n1p2 logs_encrypted
While /etc/crypttab
is commonly associated with automatic boot decryption, its noauto
option makes it perfect for manual scenarios:
# /etc/crypttab example configuration
db_crypt UUID=1234-5678 none noauto
logs_crypt /dev/nvme0n1p2 none noauto
backup_crypt /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_3 none noauto
Here's a bash script that parses /etc/crypttab
to simplify LUKS operations:
#!/bin/bash
# crypttab-helper: Wrapper for cryptsetup using /etc/crypttab
if [ $# -ne 1 ]; then
echo "Usage: ${0##*/} <mapping_name>"
exit 1
fi
mapping_name=$1
found=0
while read -r name device keyfile options; do
# Skip comments and empty lines
[[ "$name" =~ ^# ]] || [[ -z "$name" ]] && continue
if [[ "$name" == "$mapping_name" ]]; then
found=1
# Handle UUID= format
if [[ "$device" =~ ^UUID= ]]; then
uuid=${device#UUID=}
device="/dev/disk/by-uuid/$uuid"
fi
echo "Opening LUKS device $device as $name"
cryptsetup luksOpen "$device" "$name"
# Check if the open operation succeeded
if [ $? -eq 0 ]; then
echo "Successfully mapped $name"
else
echo "Failed to open $name" >&2
exit 2
fi
fi
done < /etc/crypttab
if [ $found -eq 0 ]; then
echo "Mapping name $mapping_name not found in /etc/crypttab" >&2
exit 3
fi
For more complex scenarios, consider these enhancements:
# Batch processing all noauto volumes
grep 'noauto' /etc/crypttab | awk '{print $1}' | xargs -n1 crypttab-helper
# Systemd service unit for delayed decryption
[Unit]
Description=Decrypt LUKS volumes marked noauto
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/crypttab-helper db_crypt
ExecStart=/usr/local/bin/crypttab-helper logs_crypt
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
When implementing this solution:
- Set strict permissions on the helper script (
chmod 700
) - Consider using key files instead of interactive passwords
- Audit
/etc/crypttab
permissions (should be 0600 root:root) - For sensitive systems, implement additional authentication before running the script
If the wrapper script doesn't meet your needs:
- Network-Based Decryption: Configure dropbear-initramfs for SSH during boot
- Tang Binding: Use Clevis with Tang servers for automated network-based decryption
- TPM Integration: Modern systems can use TPM2 for sealed key release
When managing encrypted volumes on headless servers or virtual machines, manually executing cryptsetup luksOpen
for each device after every reboot becomes tedious. The standard boot-time decryption process fails when:
- No console access exists during boot
- Automated password entry isn't feasible
- You need selective device mapping
The crypttab format contains four fields per entry:
<name> <device> <password> <options>
For our use case, a typical entry would look like:
data_crypt UUID=12345678-9abc-def0-1234-56789abcdef0 none noauto
Unlike mount
which reads /etc/fstab
, cryptsetup lacks direct crypttab parsing. We need to bridge this gap with automation.
Create /usr/local/bin/crypttab-open
:
#!/bin/bash
# Read crypttab and open specified mapping
name=$1
while read -r m_name m_device m_password m_options; do
[[ "$m_name" == "$name" ]] || continue
[[ "$m_device" =~ ^UUID= ]] && m_device="/dev/disk/by-uuid/${m_device#UUID=}"
cryptsetup luksOpen "$m_device" "$m_name"
exit $?
done < /etc/crypttab
exit 1
Make it executable:
chmod +x /usr/local/bin/crypttab-open
Single device mapping:
crypttab-open data_crypt
Batch mapping all noauto devices:
awk '/noauto/ {print $1}' /etc/crypttab | xargs -n1 crypttab-open
For systems with complex requirements, consider this enhanced version:
#!/bin/bash
# Enhanced crypttab opener with error handling
set -e
CRYPTTAB=${CRYPTTAB:-/etc/crypttab}
open_mapping() {
local name=$1
local found=0
while IFS=" " read -r m_name m_device m_password m_options; do
[[ "$m_name" == "$name" ]] || continue
found=1
# Skip commented lines
[[ "$m_name" =~ ^# ]] && continue
# Handle UUID devices
if [[ "$m_device" =~ ^UUID= ]]; then
m_device="/dev/disk/by-uuid/${m_device#UUID=}"
fi
# Skip already active mappings
if cryptsetup status "$m_name" &>/dev/null; then
echo "Mapping $m_name already active"
return 0
fi
# Execute the open command
echo "Opening $m_name ($m_device)"
cryptsetup luksOpen "$m_device" "$m_name"
return $?
done < "$CRYPTTAB"
[[ $found -eq 0 ]] && echo "Error: Mapping $name not found in $CRYPTTAB" >&2
return 1
}
# Main execution
for name in "$@"; do
open_mapping "$name"
done
Create /etc/systemd/system/crypttab-open@.service
:
[Unit]
Description=Open LUKS mapping %i from crypttab
[Service]
Type=oneshot
ExecStart=/usr/local/bin/crypttab-open %i
Now you can trigger mappings via:
systemctl start crypttab-open@data_crypt.service