Script backup luks header
This commit is contained in:
		@@ -144,4 +144,83 @@ So, in conclusion, I:
 | 
				
			|||||||
- Learned that Qwen on a RTX 5090 is more coherent than ChatGPT-4o on server farms (not even mentioning “normal” ChatGPT)
 | 
					- Learned that Qwen on a RTX 5090 is more coherent than ChatGPT-4o on server farms (not even mentioning “normal” ChatGPT)
 | 
				
			||||||
- Learned that even with 100TB of storage, monitoring it would’ve alerted me much earlier to the 12TB of duplicates lying around
 | 
					- Learned that even with 100TB of storage, monitoring it would’ve alerted me much earlier to the 12TB of duplicates lying around
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Catch you next time for more exciting adventures.
 | 
					## Backup of LUKS Headers for Encrypted Disks/Volumes
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I recently realized that having just the password is not enough to unlock a LUKS volume after a failure or corruption. I learned how to dump the LUKS headers from disks/volumes and to use the serial numbers along with partition names to accurately identify which header corresponds to which disk/partition (I have 10 of them!).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After struggling to do this manually, I asked Qwen3 (an LLM running on my RTX 5090) to create a script that automates the listing and identification of disks, dumps the headers, and stores them in an encrypted archive ready to be backed up on my backup server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This script:
 | 
				
			||||||
 | 
					* Lists and identifies disks with their serial numbers
 | 
				
			||||||
 | 
					* Lists partitions
 | 
				
			||||||
 | 
					* Dumps headers into a secured folder under `/root`
 | 
				
			||||||
 | 
					* Creates a temporary archive
 | 
				
			||||||
 | 
					* Prompts for a password
 | 
				
			||||||
 | 
					* Encrypts the archive with that password
 | 
				
			||||||
 | 
					* Deletes the unencrypted archive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Directory where LUKS headers will be backed up
 | 
				
			||||||
 | 
					DEST="/root/luks-headers-backup"
 | 
				
			||||||
 | 
					mkdir -p "$DEST"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "🔍 Searching for LUKS containers on all partitions..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Loop through all possible disk partitions (including NVMe and SATA)
 | 
				
			||||||
 | 
					for part in /dev/sd? /dev/sd?? /dev/nvme?n?p?; do
 | 
				
			||||||
 | 
					    # Skip if the device doesn't exist
 | 
				
			||||||
 | 
					    if [ ! -b "$part" ]; then
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Check if the partition is a LUKS encrypted volume
 | 
				
			||||||
 | 
					    if cryptsetup isLuks "$part"; then
 | 
				
			||||||
 | 
					        # Find the parent disk device (e.g. nvme0n1p4 → nvme0n1)
 | 
				
			||||||
 | 
					        disk=$(lsblk -no pkname "$part" | head -n 1)
 | 
				
			||||||
 | 
					        full_disk="/dev/$disk"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Get the serial number of the parent disk
 | 
				
			||||||
 | 
					        SERIAL=$(udevadm info --query=all --name="$full_disk" | grep ID_SERIAL= | cut -d= -f2)
 | 
				
			||||||
 | 
					        if [ -z "$SERIAL" ]; then
 | 
				
			||||||
 | 
					            SERIAL="unknown"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Extract the partition name (e.g. nvme0n1p4)
 | 
				
			||||||
 | 
					        PART_NAME=$(basename "$part")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Build the output filename with partition name and disk serial
 | 
				
			||||||
 | 
					        OUTPUT="$DEST/luks-header-${PART_NAME}__${SERIAL}.img"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        echo "🔐 Backing up LUKS header of $part (Serial: $SERIAL)..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Backup the LUKS header to the output file
 | 
				
			||||||
 | 
					        cryptsetup luksHeaderBackup "$part" --header-backup-file "$OUTPUT"
 | 
				
			||||||
 | 
					        if [[ $? -eq 0 ]]; then
 | 
				
			||||||
 | 
					            echo "✅ Backup successful → $OUTPUT"
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            echo "❌ Backup failed for $part"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create a timestamped compressed tar archive of all header backups
 | 
				
			||||||
 | 
					ARCHIVE_NAME="/root/luks-headers-$(date +%Y%m%d_%H%M%S).tar.gz"
 | 
				
			||||||
 | 
					echo "📦 Creating archive $ARCHIVE_NAME..."
 | 
				
			||||||
 | 
					tar -czf "$ARCHIVE_NAME" -C "$DEST" .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Encrypt the archive symmetrically using GPG with AES256 cipher
 | 
				
			||||||
 | 
					echo "🔐 Encrypting the archive with GPG..."
 | 
				
			||||||
 | 
					gpg --symmetric --cipher-algo AES256 "$ARCHIVE_NAME"
 | 
				
			||||||
 | 
					if [[ $? -eq 0 ]]; then
 | 
				
			||||||
 | 
					    echo "✅ Encrypted archive created: ${ARCHIVE_NAME}.gpg"
 | 
				
			||||||
 | 
					    # Remove the unencrypted archive for security
 | 
				
			||||||
 | 
					    rm -f "$ARCHIVE_NAME"
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    echo "❌ Encryption failed"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Don’t forget to back up `/etc/fstab` and `/etc/crypttab` as well!**
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user