nbc supports full disk encryption using LUKS2 with optional TPM2 automatic unlock.
Quick Start
# Install with encryption (passphrase only)
nbc install --encrypt --passphrase "your-secure-passphrase" ghcr.io/myorg/myimage:latest /dev/sda
# Install with encryption + TPM2 automatic unlock
nbc install --encrypt --passphrase "your-secure-passphrase" --tpm2 ghcr.io/myorg/myimage:latest /dev/sda
How It Works
Encrypted Partitions
When --encrypt is specified, the following partitions are encrypted with LUKS2:
| Partition | Mapper Name | Description |
|---|---|---|
| Root1 | /dev/mapper/root1 |
Active root filesystem |
| Root2 | /dev/mapper/root2 |
Inactive root for A/B updates |
| Var | /dev/mapper/var |
Persistent /var data |
The ESP (EFI System Partition) and Boot partitions are not encrypted to allow the bootloader to load the kernel and initramfs.
Encryption Flow
- Partition Creation: Standard GPT partitions are created
- LUKS Setup: Each partition (root1, root2, var) is formatted with LUKS2
- LUKS Open: Encrypted containers are opened to
/dev/mapper/<name> - Filesystem Creation: ext4 (or btrfs) filesystem is created on the mapper device
- Container Extraction: OS is extracted to the mounted filesystems
- Crypttab Generation:
/etc/crypttabis generated for boot-time unlock - Bootloader Config: Kernel arguments are configured for LUKS unlock
- TPM2 Enrollment (optional): TPM2 key is enrolled for automatic unlock
Kernel Arguments
With encryption enabled, the bootloader is configured with:
root=/dev/mapper/root1 rw rd.luks.uuid=<root1-luks-uuid> rd.luks.name=<uuid>=root1 systemd.mount-extra=/dev/mapper/var:/var:ext4:defaults
With TPM2 enabled, rd.luks.options=tpm2-device=auto is added to enable automatic unlock.
TPM2 Automatic Unlock
When --tpm2 is specified:
- TPM2 key is enrolled using
systemd-cryptenroll - No PCR binding is used (empty PCRs = unlock regardless of boot state)
- The passphrase remains as a backup unlock method
- Initramfs automatically uses TPM2 to unlock the root partition
Why No PCR Binding?
PCR (Platform Configuration Register) binding ties the encryption key to specific boot measurements. While this provides additional security, it can cause lockout when:
- Kernel is updated
- Initramfs is regenerated
- Bootloader configuration changes
- Firmware updates occur
By using empty PCRs (--tpm2-pcrs=), the system will unlock as long as:
- The TPM2 chip is present
- The TPM2 state hasn't been reset
- No tampering is detected
The passphrase always works as a fallback.
Container Image Requirements
Your bootc container image must include LUKS and TPM2 support in the initramfs.
Debian/Ubuntu
RUN apt-get install -y \
cryptsetup \
cryptsetup-initramfs \
tpm2-tools
# Rebuild initramfs with LUKS support
RUN update-initramfs -u -k all
Required packages:
cryptsetup- LUKS userspace toolscryptsetup-initramfs- LUKS hook for initramfs-toolstpm2-tools- TPM2 userspace tools (for TPM2 unlock)libtss2-tcti-device0- TPM2 TCTI library (often auto-installed)
Fedora/RHEL/CentOS
RUN dnf install -y \
cryptsetup \
tpm2-tools \
tpm2-tss
# Dracut should auto-include crypt module
RUN dracut --force --regenerate-all
Required packages:
cryptsetup- LUKS userspace toolstpm2-tools- TPM2 userspace toolstpm2-tss- TPM2 software stack
Dracut modules (should be included automatically):
90crypt- LUKS support91tpm2-tss- TPM2 support
Verification
During Installation
nbc will check for LUKS/TPM2 support after extracting the container and warn if:
- No LUKS initramfs support is detected
- TPM2 is requested but TPM2 initramfs support is not detected
These are warnings, not errors, since detection is best-effort.
After Boot
Check encryption status:
# Verify root is on a LUKS device
lsblk -f | grep crypt
# Check LUKS header
cryptsetup luksDump /dev/sdaX
# Verify TPM2 enrollment
systemd-cryptenroll --tpm2-device=list /dev/sdaX
Troubleshooting
Boot Prompts for Passphrase (TPM2 Not Working)
- Check if TPM2 is available:
ls /dev/tpm* - Verify TPM2 enrollment:
systemd-cryptenroll /dev/sdaX - Check kernel args include
rd.luks.options=tpm2-device=auto - Ensure initramfs has TPM2 support
Cannot Boot After Update
If A/B update breaks TPM2 unlock:
- Enter passphrase at boot prompt
- Re-enroll TPM2:
systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto /dev/sdaX
Emergency Recovery
Boot from live media and:
# Unlock with passphrase
cryptsetup luksOpen /dev/sdaX root1
# Mount and fix
mount /dev/mapper/root1 /mnt
Security Considerations
-
Passphrase Strength: Use a strong passphrase. It's your backup when TPM2 fails.
-
Physical Access: Without PCR binding, anyone with physical access to the TPM2 can unlock the system. The security comes from:
- TPM2 is bound to the specific hardware
- Removing the disk and attaching to another machine won't work
- The passphrase is still required without TPM2
-
Recovery Key: Consider adding a recovery key:
systemd-cryptenroll --recovery-key /dev/sdaX -
Remote Unlock: For servers, consider adding network-based unlock (NBDE/Tang).
Implementation Details
Files Created
| File | Purpose |
|---|---|
/etc/crypttab |
Defines LUKS devices for systemd |
/boot/loader/entries/*.conf |
Boot entry with LUKS kernel args |
/boot/grub/grub.cfg |
GRUB config with LUKS kernel args |
LUKS Format Options
nbc uses LUKS2 with default settings:
cryptsetup luksFormat --type luks2- Default cipher (typically
aes-xts-plain64) - Default key size (typically 256-bit)
- Default PBKDF (argon2id)
Mapper Device Names
| Partition Role | Mapper Name |
|---|---|
| Root1 (active) | root1 |
| Root2 (inactive) | root2 |
| Var | var |
These names are used in:
/dev/mapper/<name>paths/etc/crypttabentries- Kernel command line arguments
System Configuration Storage
Encryption configuration is stored in /var/lib/nbc/state/config.json to support A/B updates:
{
"image_ref": "ghcr.io/example/image:latest",
"device": "/dev/sda",
"encryption": {
"enabled": true,
"tpm2": true,
"root1_luks_uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"root2_luks_uuid": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"var_luks_uuid": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"
}
}
This configuration is:
- Created during install: LUKS UUIDs for all partitions are stored
- Read during update: Used to generate correct kernel arguments for each root
- Essential for A/B updates: Each root partition has a different LUKS UUID
A/B Updates with Encryption
When updating an encrypted system, nbc update automatically:
- Reads the encryption config from
/var/lib/nbc/state/config.json - Determines which root partition is active and which is the target
- Generates kernel command lines with the correct LUKS UUIDs:
- Target root: Uses root1 or root2 LUKS UUID based on which is inactive
- Previous root: Uses the active root's LUKS UUID for rollback
- Var partition: Always uses the same var LUKS UUID
Example kernel command line for encrypted target:
root=/dev/mapper/root2 rw rd.luks.uuid=<root2-uuid> rd.luks.name=<root2-uuid>=root2 rd.luks.uuid=<var-uuid> rd.luks.name=<var-uuid>=var rd.luks.options=<root2-uuid>=tpm2-device=auto rd.luks.options=<var-uuid>=tpm2-device=auto systemd.mount-extra=/dev/mapper/var:/var:ext4:defaults