Signal9Signal9 — home
online
all lessons

Encrypted Filesystems with LUKS (cryptsetup)

Advanced30 min7 steps
What you'll learn
  • Explain what LUKS/dm-crypt protects (data at rest) and what the LUKS header stores
  • Create, open, format, and mount an encrypted block device with cryptsetup
  • Read a volume's cipher specification and key slots from cryptsetup luksDump
  • Make an encrypted volume open automatically at boot with /etc/crypttab and /etc/fstab
  • Back up the LUKS header and explain why losing it destroys the data
  1. What LUKS encrypts, and what it doesn't

    Place LUKS in the stack: dm-crypt does the encryption, LUKS manages the keys, and it all protects data AT REST.

    LUKS (Linux Unified Key Setup) is the standard way to encrypt a block device on Linux. It sits on two pieces: dm-crypt, the kernel device-mapper target that transparently encrypts and decrypts every block as it's written and read, and the LUKS header, an on-disk metadata area that stores the cipher choice and the key slots. What it protects is data at rest: pull the disk, image it, or steal the laptop, and without the passphrase the bytes are noise. What it does not protect is a running, unlocked system — once the volume is open, the filesystem is plaintext to anyone with access, exactly like any other mount.

    The key detail that trips people up: LUKS does not encrypt your data with your passphrase. The disk is encrypted with a random master key, generated once at format time. Your passphrase unlocks a key slot that holds an encrypted copy of that master key. That indirection is why you can have up to eight passphrases for one volume, change a passphrase without re-encrypting a terabyte of data, and why the header is everything — lose it, and the master key is gone with it (more on that at the end).

  2. Create an encrypted volume: cryptsetup luksFormat (run on a real host or VM)

    Initialise a block device as LUKS, writing the header and the first key slot.

    Before you type anything: the commands in steps 1–5 need a real host or VM with the device-mapper stack, and cryptsetup is not installed on the lab box — so don't expect them to run in your lab terminal (you'll get cryptsetup: command not found, and it isn't installable there). Read them here and run them on a real machine — step 6 has a loopback recipe that needs no spare disk — then come back to the lab box for the header-reading half. The lab box is for the read-only side of LUKS; the write side is real-hardware work.

    luksFormat writes a fresh LUKS header onto the device and sets up key slot 0 from a passphrase you choose. It is destructive — it overwrites whatever was there — so it asks you to confirm by typing YES in capitals. On a modern system you get LUKS2 and a sensible default cipher without specifying one.

    sudo cryptsetup luksFormat /dev/sdb1
    WARNING: Device /dev/sdb1 already contains a 'ext4' superblock signature.
    
    WARNING!
    ========
    This will overwrite data on /dev/sdb1 irrevocably.
    
    Are you sure? (Type 'yes' in capital letters): YES
    Enter passphrase for /dev/sdb1: 
    Verify passphrase: 
    luksFormat
    Initialise the named device as a LUKS container and write its header.
    -c / --cipher
    Override the cipher spec, e.g. -c aes-xts-plain64. Omit it to take the built-in default.
    -s / --key-size
    Key size in bits, e.g. -s 512 (XTS splits it: 512 = two 256-bit keys).
    This creates the container but plants no filesystem yet — a LUKS device is a wrapper you still have to open and format. Typing YES is a deliberate speed bump because there is no undo. The passphrase you set here fills key slot 0; you can add more later with luksAddKey.
  3. Open it, format it, mount it

    Unlock the container to a /dev/mapper node, put a filesystem on it, and mount it.

    An encrypted device is opened to a mapper name under /dev/mapper/. That mapper node is the plaintext view — you make a filesystem on it and mount it exactly like a normal partition, and dm-crypt encrypts every block on the way down to /dev/sdb1.

    sudo cryptsetup luksOpen /dev/sdb1 secure
    Enter passphrase for /dev/sdb1: 
    luksOpen (aka open)
    Unlock the container and expose it as /dev/mapper/<name> after a correct passphrase.
    After this succeeds, /dev/mapper/secure exists and is the decrypted device. ls -l /dev/mapper/secure and lsblk both show the mapping (sdb1 → secure (crypt)). Nothing is on it yet.

    Example — not runnable on your lab box

    sudo mkfs.ext4 /dev/mapper/secure && sudo mount /dev/mapper/secure /mnt/secure
    mke2fs 1.46.5 (30-Dec-2021)
    Creating filesystem with 262144 4k blocks ...
    done
    You format and mount the mapper node, never the raw /dev/sdb1 — writing a filesystem straight onto the encrypted device would clobber the LUKS header. Files written under /mnt/secure are ciphertext on the disk and plaintext through the mount.
  4. Read the crypto: cryptsetup luksDump

    Inspect a volume's header to report its cipher specification and key slots.

    luksDump prints the header without unlocking anything — no passphrase needed, because the header itself is not secret (only the key slots are, and those are encrypted). It is how you answer 'what cipher is this volume using?' on an incident report: the cipher name (e.g. aes), the cipher mode (xts-plain64), and the key size. The full specification a report wants is the joined form, aes-xts-plain64.

    sudo cryptsetup luksDump /dev/sdb1
    LUKS header information
    Version:        2
    Epoch:          3
    Cipher name:    aes
    Cipher mode:    xts-plain64
    Key size:       512 [bits]
    Data segments:
      0: crypt
    	cipher: aes-xts-plain64
    Keyslots:
      0: luks2
    	Key:        512 bits
    luksDump
    Print the LUKS header: version, cipher, key size, and each occupied key slot.
    --dump-master-key
    (Dangerous) also print the master key — needed for header recovery, never for routine reads.
    Read Cipher name + Cipher mode together for the full spec — here aes-xts-plain64. Key size: 512 [bits] is XTS splitting into two 256-bit AES keys, so this is AES-256. The Keyslots section shows which of the eight slots hold a passphrase; a volume with several slots simply has several people/keys able to open it.
  5. Make it open at boot: /etc/crypttab and /etc/fstab

    Persist an encrypted mount so it survives a reboot, the way a real system runs it.

    By itself an opened volume is gone after a reboot — the mapping lives in kernel memory. Two files make it persistent, and they work as a pair. /etc/crypttab says how to unlock the device into a mapper name; /etc/fstab then mounts that mapper name like any other filesystem. crypttab is read before fstab at boot precisely so the mapper node exists by the time fstab wants it.

    echo 'secure  UUID=<sdb1-uuid>  none  luks' | sudo tee -a /etc/crypttab
    secure  UUID=1b3f...  none  luks
    field 1 (name)
    The mapper name to create: becomes /dev/mapper/secure.
    field 2 (device)
    The backing block device — prefer UUID= over /dev/sdb1, which can renumber.
    field 3 (key)
    A keyfile path, or none to prompt for the passphrase at boot.
    field 4 (options)
    luks marks it a LUKS device; add options like discard here.
    Use the device's UUID (blkid /dev/sdb1), not the kernel name — /dev/sdb and /dev/sdc can swap between boots and you'd unlock the wrong disk. none in field 3 means 'ask me for the passphrase at boot'; a keyfile path there unlocks it unattended.
    echo '/dev/mapper/secure  /mnt/secure  ext4  defaults  0 2' | sudo tee -a /etc/fstab
    /dev/mapper/secure  /mnt/secure  ext4  defaults  0 2
    fstab mounts the mapper node, never /dev/sdb1. Validate the pair without rebooting: sudo cryptdisks_start secure (or systemctl start systemd-cryptsetup@secure) then sudo mount -a — if both succeed cleanly, the boot will too.
  6. Close it, and guard the header

    Tear down a mapping safely, and understand why the header is the whole ballgame.

    Sealing a volume back up is the mirror image of opening it, and the order matters: unmount the filesystem first, then close the mapping. Skip the unmount and the close fails because the device is still in use. But the more important idea in this step is why an encrypted volume is so unforgiving of header damage — the same indirection that makes LUKS flexible also makes the header the one thing you cannot lose.

    Example — not runnable on your lab box

    sudo umount /mnt/secure && sudo cryptsetup luksClose secure
    luksClose (aka close)
    Remove the /dev/mapper mapping — the plaintext view disappears; the data is sealed again.
    Always umount before luksClose — closing a mapping with a filesystem still mounted on it will fail ('Device secure is still in use'). After close, /dev/mapper/secure is gone and only ciphertext remains on /dev/sdb1.

    Check yourself

    You run luksFormat again on a device that already holds an encrypted filesystem you care about. What happens to the existing data?

  7. On the lab box: read the header, and know what a container can't do

    Read a LUKS volume's crypto from a captured header on the lab box, and understand why the create/open lifecycle needs a real machine.

    The lab box is a container, and that changes what of this lesson you can run here. cryptsetup is not installed on the lab image (and apt-get install cryptsetup reports "no installation candidate", so you can't add it), /dev/mapper holds only its control node, and there is no spare disk to format. That is not a gap in the room — dm-crypt is a kernel device-mapper target, and creating a mapping needs the module, the device-mapper stack, and a real block device. Steps 1–5 are how you do this on a real host or VM; run them there, on a spare disk or a loopback file, where every one of them works.

    What the lab box is genuinely good for is the half of LUKS that analysis and exam tasks actually ask about: reading a header you were handed. You never need to unlock a volume — or even have cryptsetup — to answer "what cipher is this?"

    lsblk -f
    NAME  FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
    nbd0
    nbd1
    ...
    vda
    vdb                               64K     0% /.fly-upper-layer
    vdc
    -f
    Show filesystem info — FSTYPE, LABEL, UUID and mountpoint per device. The FSTYPE column is where crypto_LUKS appears.
    This is the real output on a lab box: virtio and network-block devices, and no `crypto_LUKS` row, because nothing here is encrypted. Learn the shape anyway — on a real system a LUKS container shows crypto_LUKS in the FSTYPE column, and that is how you spot an encrypted volume during triage before you ever open it.
    ls -l /dev/mapper/
    total 0
    crw------- 1 root root 10, 236 Sep 18 11:19 control
    -l
    Long listing — shows each entry's type, so you can tell the control character device from the symlinks that opened volumes create.
    Only the control node — no mappings exist and none can be created here. On a real machine each volume you luksOpen appears in this directory as a named node (/dev/mapper/secure), which is the node you then mkfs and mount. Seeing just control is how you confirm nothing is currently unlocked.

    Runnable in the st10 room — that room’s setup plants the files it reads

    grep -iE 'cipher|Key:' /root/storage/luksdump.txt
    cipher: aes-xts-plain64
            Key:        512 bits
    -i
    Case-insensitive — luksDump output capitalises inconsistently across versions (cipher: vs Cipher name:).
    -E
    Extended regex, so | means alternation — match the cipher line or the key-size line in one pass.
    An analysis room hands you a captured luksDump instead of a live volume — in Storage Analysis: LUKS Cipher it is at /root/storage/luksdump.txt. This is the whole read: the cipher: line already carries the full spec (aes-xts-plain64 = cipher-chainmode-iv), and Key: 512 bits means AES-256 under XTS, because XTS splits the key into two halves. That pair is exactly the report line an encryption-analysis task wants.
Recap
  • LUKS = dm-crypt (per-block encryption) + a header holding the cipher and the key slots; your passphrase unlocks a slot that decrypts the random master key, it does not encrypt the data directly.
  • Lifecycle is one tool: luksFormat (create, destructive) -> luksOpen to /dev/mapper/<name> -> mkfs + mount the MAPPER node -> luksClose. Never format or mount the raw device.
  • luksDump reads the header with no passphrase: join Cipher name + Cipher mode for the spec (aes-xts-plain64), and Key size 512 = AES-256 under XTS.
  • Persist with /etc/crypttab (unlock -> mapper name, by UUID) read before /etc/fstab (mount the mapper); back up the header, because losing it destroys the volume no matter how many passphrases you know.