Keystone

Self-sovereign infrastructure platform with secure, encrypted NixOS deployments


Project maintained by ncrmro Hosted on GitHub Pages — Theme by mattgraham

TPM Enrollment Guide

Version: 1.0 Date: 2025-11-03 Module: keystone.tpmEnrollment

Overview

This guide explains how to configure TPM-based automatic disk unlock for your Keystone installation. TPM (Trusted Platform Module) enrollment allows your system to unlock encrypted disks automatically during boot without manual password entry, while maintaining secure recovery credentials for emergency scenarios.


Quick Start

After a fresh Keystone installation, you’ll see a security warning on first login:


   TPM ENROLLMENT NOT CONFIGURED                            


Choose one of these commands to get started:

# Recommended: Generate recovery key
$ sudo keystone-enroll-recovery

# Alternative: Set custom password
$ sudo keystone-enroll-password

Both commands will:

  1. Configure your chosen recovery credential
  2. Enroll TPM for automatic unlock
  3. Remove the default “keystone” password
  4. Enable automatic boot unlock

Prerequisites

Before enrolling TPM, verify these requirements:

1. Secure Boot Must Be Enabled

$ sudo bootctl status | grep "Secure Boot"
Secure Boot: enabled (user)

If output shows disabled or setup, complete Secure Boot enrollment first:

$ sudo sbctl status

2. TPM 2.0 Must Be Available

$ ls /dev/tpm*
/dev/tpm0  /dev/tpmrm0

For VMs: Enable TPM 2.0 emulation in your hypervisor For bare metal: Enable TPM in BIOS/UEFI settings

3. Credstore Volume Must Exist

$ ls /dev/zvol/rpool/credstore
/dev/zvol/rpool/credstore

This is automatically created during Keystone installation with keystone.disko.enable = true.


Enrollment Methods

Recovery keys provide maximum security with minimal memorization burden.

Run:

$ sudo keystone-enroll-recovery

You will receive a recovery key like:

fda7-w4n8-km9p-3jc2-vx5h-7qte-2nuw-8rbg

CRITICAL: Save this key immediately in:

Advantages:

Disadvantages:


Method 2: Custom Password

Custom passwords are familiar and don’t require external storage.

Run:

$ sudo keystone-enroll-password

Requirements:

Password Examples:

 GOOD: "coffee-morning-laptop-window" (28 characters)
 GOOD: "MyBlueServer2024Today" (20 characters)
 GOOD: "xK9mP2vL4nQ8wR7tY3nB" (20 characters)
 BAD:  "Password1!" (11 characters - too short)
 BAD:  "keystone" (prohibited - publicly known)

Advantages:

Disadvantages:


What Happens During Enrollment

Both enrollment methods follow this workflow:

  1. Prerequisite Validation
    • Verify Secure Boot enabled in User Mode
    • Verify TPM 2.0 device available
    • Verify credstore volume exists
  2. Credential Setup
    • Recovery key: Generate cryptographically secure key
    • Custom password: Prompt and validate password (12-64 chars)
    • Add credential to LUKS keyslot
  3. TPM Enrollment
    • Configure TPM unlock using PCRs 1,7 (configurable)
    • Store sealed unlock key in LUKS header
    • PCR 1: Firmware configuration
    • PCR 7: Secure Boot certificates
  4. Security Cleanup
    • Verify TPM enrollment succeeded
    • Remove default “keystone” password
    • Create enrollment marker file
  5. Completion
    • Display confirmation message
    • Suppress future login banners

After enrollment, the system will unlock automatically during normal boots.


Recovery Scenarios

When Will I Need My Recovery Credential?

You will need your recovery key or custom password in these situations:

Hardware Changes

Firmware Changes

Software Changes (with PCRs 1,7)

Disaster Scenarios


Re-Enrolling TPM After Changes

If you intentionally changed firmware or Secure Boot configuration:

# 1. Boot with recovery key/custom password (system will prompt)

# 2. Verify Secure Boot is enabled
$ sudo bootctl status | grep "Secure Boot: enabled"

# 3. Remove old TPM keyslot (bound to old PCR values)
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# 4. Enroll new TPM keyslot with updated PCR values
$ sudo keystone-enroll-tpm

# 5. Reboot to verify automatic unlock
$ sudo reboot

After reboot, the system should unlock automatically with the new PCR measurements.


Testing Your Recovery Credential

IMPORTANT: Test your recovery key/password BEFORE you need it:

Test Recovery Key

# 1. Temporarily disable TPM unlock
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# 2. Reboot - system will prompt for password
$ sudo reboot

# Boot prompt will appear:
# Please enter passphrase for disk credstore: _

# 3. Enter your recovery key at the prompt

# 4. After successful login, re-enable TPM
$ sudo keystone-enroll-tpm

# 5. Reboot to verify automatic unlock works again
$ sudo reboot

Test Custom Password

Same procedure as recovery key testing above - enter your custom password at the boot prompt.


Advanced Configuration

Customizing PCR List

By default, TPM enrollment uses PCRs 1 and 7. You can customize this in your NixOS configuration:

# configuration.nix
{
  keystone.tpmEnrollment = {
    enable = true;

    # Custom PCR list - choose based on your security vs update trade-off
    tpmPCRs = [ 7 ];  # Secure Boot only (most update-resilient)
    # tpmPCRs = [ 1 7 ];  # Default: Firmware config + Secure Boot
    # tpmPCRs = [ 0 1 7 ];  # More restrictive: Firmware code + config + Secure Boot
  };
}

PCR Selection Guide:

PCR List Security Update Resilience When to Use
[7] Good Excellent Frequent firmware updates, prioritize convenience
[1 7] Better Good Default balanced approach (recommended)
[0 1 7] Best Poor Maximum security, rare firmware updates
[7 11] Excellent Poor Requires signed PCR policies (future feature)

Trade-offs:

Custom Credstore Device

If you modified the disko configuration to use a different credstore path:

{
  keystone.tpmEnrollment = {
    enable = true;
    credstoreDevice = "/dev/mapper/my-custom-credstore";
  };
}

Troubleshooting

Problem: Banner Still Appears After Enrollment

Symptoms: Login banner shows “TPM ENROLLMENT NOT CONFIGURED” even after running enrollment

Diagnosis:

# Check if TPM keyslot exists
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore | grep systemd-tpm2

# Check marker file
$ cat /var/lib/keystone/tpm-enrollment-complete

Solutions:


Problem: System Prompts for Password on Every Boot

Symptoms: After enrollment, system still asks for password at boot

Diagnosis:

# Check boot logs for TPM unlock attempts
$ sudo journalctl -b | grep -i "credstore\|tpm"

# Expected: "Failed to activate with TPM2" (indicates PCR mismatch)

Common Causes:

  1. Secure Boot disabled: PCR 7 value changed
    • Fix: Re-enable Secure Boot in BIOS, reboot
  2. Firmware updated: PCR values changed
    • Fix: Re-enroll TPM (see “Re-Enrolling TPM” section)
  3. TPM enrollment failed silently: No TPM keyslot created
    • Fix: Re-run enrollment command, check for errors
  4. Wrong PCR configuration: PCRs configured don’t match boot state
    • Fix: Adjust tpmPCRs in configuration, re-enroll

Problem: “No TPM2 Device Found” Error

For Virtual Machines:

libvirt/virt-manager:

# Check if TPM is enabled in VM XML
$ virsh dumpxml your-vm-name | grep -A 3 "<tpm"

# If missing, add TPM device:
$ virsh edit your-vm-name

# Add this in <devices> section:
#   <tpm model='tpm-crb'>
#     <backend type='emulator' version='2.0'/>
#   </tpm>

QEMU directly:

# Add TPM emulation to QEMU command:
qemu-system-x86_64 \
  -tpmdev emulator,id=tpm0,chardev=chrtpm \
  -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
  -device tpm-crb,tpmdev=tpm0 \
  ...

bin/virtual-machine script: The Keystone bin/virtual-machine script automatically includes TPM 2.0 emulation.

For Bare Metal:

  1. Reboot into BIOS/UEFI firmware settings
  2. Look for “Security” or “Advanced” section
  3. Enable “TPM Device” or “TPM 2.0”
  4. Save and reboot
  5. Verify: ls /dev/tpm*

Problem: “Secure Boot Not Enabled” Error

Check current status:

$ sudo bootctl status
Secure Boot: disabled (setup)
Setup Mode: setup

If in Setup Mode:

  1. Secure Boot keys not yet enrolled
  2. Run Keystone Secure Boot enrollment (done during installation)
  3. Reboot to activate User Mode

If Secure Boot disabled in BIOS:

  1. Reboot into BIOS/UEFI settings
  2. Enable “Secure Boot”
  3. Save and reboot
  4. Verify: sudo bootctl status

Security Considerations

Why Remove Default Password?

The default “keystone” password is:

Enrollment scripts remove this password after adding your secure credential.

Why TPM + Recovery Credential?

TPM Automatic Unlock:

Recovery Credential (recovery key or custom password):

Both together provide defense-in-depth:

PCR Selection Security

Default PCRs 1,7 Provide:

Default PCRs 1,7 Do NOT Protect Against:

For enhanced security, consider:


Configuration Reference

Module Options

# configuration.nix
{
  keystone.tpmEnrollment = {
    # Enable the TPM enrollment module
    enable = true;

    # PCR list for TPM binding (default: [1 7])
    tpmPCRs = [ 1 7 ];

    # Credstore device path (default matches disko module)
    credstoreDevice = "/dev/zvol/rpool/credstore";
  };
}

Available Commands

After enabling the module, these commands become available:

Command Purpose
keystone-enroll-recovery Generate recovery key + enroll TPM
keystone-enroll-password Set custom password + enroll TPM
keystone-enroll-tpm Enroll TPM only (advanced users)

File Locations

File Purpose
/var/lib/keystone/tpm-enrollment-complete Enrollment status marker
/dev/zvol/rpool/credstore LUKS-encrypted credstore volume
/etc/profile.d/tpm-enrollment-warning.sh Login banner script

Frequently Asked Questions

Q: Can I enroll both recovery key AND custom password?

Yes! LUKS supports up to 32 keyslots. You can run:

$ sudo keystone-enroll-recovery
# Save recovery key

$ sudo systemd-cryptenroll --password /dev/zvol/rpool/credstore
# Enter custom password

Both credentials will work for recovery scenarios.

Q: How do I change my custom password?

# Add new password (will prompt for current password)
$ sudo systemd-cryptenroll --password /dev/zvol/rpool/credstore

# Remove old password (optional - you can keep both)
$ sudo systemd-cryptenroll --wipe-slot=1 /dev/zvol/rpool/credstore

Q: How do I view current LUKS keyslots?

$ sudo systemd-cryptenroll /dev/zvol/rpool/credstore
SLOT TYPE
   1 recovery
   2 tpm2

# Or detailed view:
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore

Q: Can I disable TPM automatic unlock?

# Remove TPM keyslot
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

# System will now prompt for password on every boot

Q: What if I lose my recovery key?

Prevention: Save recovery key in multiple secure locations immediately

If lost before TPM failure: Generate new recovery key while system is accessible:

$ sudo systemd-cryptenroll --recovery-key /dev/zvol/rpool/credstore
# Save new key immediately

If lost after TPM failure: DATA IS UNRECOVERABLE - no way to unlock disk

Q: Can I use the same recovery key on multiple systems?

No - each system generates a unique recovery key during enrollment. This is a security feature - if one system’s key is compromised, other systems remain secure.

Q: How do I check which PCRs are currently enrolled?

$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore | grep -A 4 systemd-tpm2
  0: systemd-tpm2
    tpm2-hash-pcrs: 1+7
    tpm2-pcr-bank: sha256
    tpm2-pin: false

Q: Can I add a PIN to TPM unlock?

Currently not supported by the enrollment scripts, but can be done manually:

$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore
$ sudo systemd-cryptenroll \
    --tpm2-device=auto \
    --tpm2-pcrs=1,7 \
    --tpm2-with-pin=yes \
    /dev/zvol/rpool/credstore

This will prompt for a PIN during boot before TPM unlock.


Best Practices

1. Test Recovery Immediately

Don’t wait for an emergency - test your recovery credential right after enrollment:

# Disable TPM temporarily
$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore
$ sudo reboot

# Use recovery credential at boot prompt
# Re-enroll TPM after successful login
$ sudo keystone-enroll-tpm

2. Store Recovery Key Offline

DO:

DON’T:

3. Document Your PCR Configuration

If you customize tpmPCRs, document the choice:

{
  keystone.tpmEnrollment = {
    enable = true;
    # Using PCR 7 only for maximum update resilience
    # Acceptable for home server with physical security
    tpmPCRs = [ 7 ];
  };
}

4. Regular Testing Schedule

Test recovery quarterly:


Migration and Compatibility

Upgrading from Manual TPM Enrollment

If you manually enrolled TPM before this module existed:

The module will self-heal:

  1. Login banner script detects existing TPM keyslot
  2. Automatically creates marker file
  3. Banner stops appearing on subsequent logins

No action required - existing enrollment continues working.

Disabling the Module

To disable TPM enrollment features:

{
  keystone.tpmEnrollment.enable = false;
}

This will:

To fully remove TPM unlock:

$ sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/zvol/rpool/credstore

Technical Details

LUKS Keyslot Layout

After enrollment, typical keyslot configuration:

Slot Type Credential
0 (removed) Default “keystone” password (removed)
1 password/recovery Your recovery key or custom password
2 tpm2 TPM-sealed automatic unlock

TPM Token Structure

The TPM token stored in LUKS header contains:

Boot Process with TPM

1. UEFI firmware loads bootloader (lanzaboote)
2. TPM measures boot state into PCRs
3. systemd initrd starts
4. systemd-cryptsetup attempts unlock:
   a. Try TPM unlock (if PCRs match policy)
   b. If TPM fails, prompt for password/recovery key
5. Credstore unlocks, ZFS key loaded
6. System continues booting

Support and Troubleshooting

Debug Commands

# Check TPM device
$ ls -la /dev/tpm*

# Check TPM capabilities
$ sudo tpm2_getcap properties-variable

# Check PCR values
$ sudo tpm2_pcrread

# Check Secure Boot status
$ sudo bootctl status

# Check LUKS keyslots
$ sudo cryptsetup luksDump /dev/zvol/rpool/credstore

# Check enrollment marker
$ cat /var/lib/keystone/tpm-enrollment-complete

# Check boot logs
$ sudo journalctl -b | grep -i "credstore\|tpm"

Get Help

If you encounter issues:

  1. Check Prerequisites: Verify Secure Boot and TPM available
  2. Review Error Messages: Enrollment scripts provide detailed guidance
  3. Consult Boot Logs: journalctl -b | grep tpm
  4. Test Recovery: Ensure recovery credential works before relying on TPM

References


Version: 1.0 Last Updated: 2025-11-03 Maintainer: Keystone Project