====== Bare metal server provisioning (debian trixie) ====== Install of a Debian 13 (Trixie) on a dedicated server with: * Full LUKS encryption (except ''/boot'' and ESP) * RAID1 on two NVMe disks * LVM on LUKS * Boot UEFI **or** legacy/BIOS (auto-detected) * Remote cryptroot unlock with ''dropbear-initramfs'' ===== Target disk layout ===== nvme0n1 (419 Go) nvme1n1 (419 Go) ├── p1 : see note below ├── p1 : see note below ├── p2 : 512 Mo ────┐ ├── p2 : 512 Mo ────┐ │ ├── md0 (RAID1) → /boot (ext4) └── p3 : ~418 Go ───┤ └── p3 : ~418 Go ───┤ └── md1 (RAID1) → LUKS → LVM (vg0) ├── root (10 Go, ext4) └── thin pool p1 depends on boot mode: * UEFI : 512 Mo ESP (ef00, vfat, mounted /boot/efi), one per disk * legacy : 1 Mo BIOS boot partition (ef02, raw), one per disk ===== Procedure ===== ==== 1. Boot in rescue mode ==== Detect the rescue boot mode once, everything downstream branches on $MODE. The mode is imposed by the rescue boot (Hetzner PXE), not locally adjustable. $ if [ -d /sys/firmware/efi ]; then MODE=uefi; else MODE=legacy; fi $ echo "Boot mode: $MODE" ==== 2. Disk setup ==== $ apt update $ apt install -y mdadm cryptsetup lvm2 parted debootstrap gdisk rsync # Cleanup (/!\ this delete all data on disks /!\) $ mdadm --stop --scan $ mdadm --zero-superblock /dev/nvme0n1 2>/dev/null $ mdadm --zero-superblock /dev/nvme1n1 2>/dev/null $ wipefs -a /dev/nvme0n1 /dev/nvme1n1 $ sgdisk --zap-all /dev/nvme0n1 $ sgdisk --zap-all /dev/nvme1n1 # GPT partitioning on both disks # p1 differs by mode: # uefi -> ESP (ef00, 512M, vfat, mounted /boot/efi) # legacy -> BIOS boot (ef02, 1M, raw, used by grub-install) $ for D in /dev/nvme0n1 /dev/nvme1n1; do if [ "$MODE" = uefi ]; then sgdisk -n 1:0:+512M -t 1:ef00 -c 1:"EFI" $D else sgdisk -n 1:0:+1M -t 1:ef02 -c 1:"BIOS boot" $D fi sgdisk -n 2:0:+512M -t 2:fd00 -c 2:"boot RAID" $D sgdisk -n 3:0:0 -t 3:fd00 -c 3:"luks RAID" $D done $ partprobe ==== 3. RAID1 mdadm ==== $ mdadm --create /dev/md0 \ --level=1 --raid-devices=2 --metadata=1.2 \ --homehost=schwartz --name=boot \ /dev/nvme0n1p2 /dev/nvme1n1p2 $ mdadm --create /dev/md1 \ --level=1 --raid-devices=2 --metadata=1.2 \ --homehost=schwartz --name=cryptroot \ /dev/nvme0n1p3 /dev/nvme1n1p3 ==== 4. LUKS on md1 ==== $ cryptsetup luksFormat --type luks2 --iter-time 5000 /dev/md1 $ cryptsetup open /dev/md1 cryptroot Passphrase generated with ''pass generate luks/schwartz 30 --no-symbols'' and stored in password-store. ==== 5. LVM ==== $ pvcreate /dev/mapper/cryptroot $ vgcreate vg0 /dev/mapper/cryptroot $ lvcreate -L 10G -n root vg0 ==== 6. Formatting & partitions setup ==== # ESP only in UEFI mode; in legacy p1 is a raw BIOS boot partition (no fs) $ if [ "$MODE" = uefi ]; then mkfs.vfat -F32 -n EFI0 /dev/nvme0n1p1 mkfs.vfat -F32 -n EFI1 /dev/nvme1n1p1 fi $ mkfs.ext4 -L boot /dev/md0 $ mkfs.ext4 -L root /dev/vg0/root $ mount /dev/vg0/root /mnt $ mkdir -p /mnt/boot $ mount /dev/md0 /mnt/boot $ if [ "$MODE" = uefi ]; then mkdir -p /mnt/boot/efi mount /dev/nvme0n1p1 /mnt/boot/efi fi ==== 7. Debootstrap ==== $ debootstrap --arch amd64 trixie /mnt http://deb.debian.org/debian/ ==== 8. Chroot setup ==== $ for d in dev dev/pts proc sys run; do mount --bind /$d /mnt/$d done # efivars only exists (and is only needed) in UEFI mode $ if [ "$MODE" = uefi ]; then mount --bind /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars fi $ cp /etc/resolv.conf /mnt/etc/resolv.conf # Get UUIDs $ blkid -s UUID -o value /dev/md0 # BOOT_UUID $ blkid -s UUID -o value /dev/vg0/root # ROOT_UUID $ blkid -s UUID -o value /dev/md1 # LUKS_UUID # EFI UUIDs only relevant in UEFI mode: $ if [ "$MODE" = uefi ]; then blkid -s UUID -o value /dev/nvme0n1p1 # EFI0_UUID blkid -s UUID -o value /dev/nvme1n1p1 # EFI1_UUID fi $ chroot /mnt /bin/bash ==== 9. Base configuration (in chroot) ==== Re-evaluate the mode inside the chroot (the rescue variable is not inherited). /sys/firmware/efi is still visible here because /sys is bind-mounted from the rescue, so the detection stays consistent with section 1. $ if [ -d /sys/firmware/efi ]; then MODE=uefi; else MODE=legacy; fi # Hostname $ echo "schwartz" > /etc/hostname $ sed -i "2i 127.0.1.1\tschwartz" /etc/hosts # Locales / timezone $ apt update && apt install -y locales $ echo "en_US.UTF-8 UTF-8" > /etc/locale.gen $ echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen $ locale-gen $ echo "LANG=en_US.UTF-8" > /etc/default/locale $ ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime $ dpkg-reconfigure -f noninteractive tzdata # APT setup $ cat > /etc/apt/sources.list < ==== 10. Network configuration ==== $ apt install -y ifupdown $ cat > /etc/network/interfaces < ==== 11. crypttab, fstab, mdadm.conf ==== # crypttab $ cat > /etc/crypttab < /etc/fstab <> /etc/fstab fi # mdadm.conf $ apt install -y mdadm $ cat > /etc/mdadm/mdadm.conf < MAILADDR root EOF $ mdadm --detail --scan >> /etc/mdadm/mdadm.conf ==== 12. Kernel and boot tools ==== $ apt install -y \ linux-image-amd64 \ cryptsetup cryptsetup-initramfs \ lvm2 \ thin-provisioning-tools \ intel-microcode \ firmware-linux \ initramfs-tools \ openssh-server ==== 13. GRUB (UEFI or legacy) ==== $ echo 'GRUB_ENABLE_CRYPTODISK=y' >> /etc/default/grub $ if [ "$MODE" = uefi ]; then ## --- UEFI --- apt install -y grub-efi-amd64 efibootmgr grub-install --target=x86_64-efi \ --efi-directory=/boot/efi \ --bootloader-id=debian \ --recheck update-grub # Sync ESP2 mkdir -p /tmp/efi2 mount /dev/nvme1n1p1 /tmp/efi2 cp -av /boot/efi/* /tmp/efi2/ umount /tmp/efi2 # UEFI fallback for ESP2 efibootmgr --create \ --disk /dev/nvme1n1 --part 1 \ --label "debian-backup" \ --loader '\EFI\debian\shimx64.efi' # Reorder to make debian (ESP1) as priority # efibootmgr -o 000A,000B,... (adapt IDs from `efibootmgr`) else ## --- legacy/BIOS --- apt install -y grub-pc # Install GRUB core into the BIOS boot partition (ef02) of BOTH disks # for redundancy: if nvme0 fails, the machine still boots on nvme1. grub-install --target=i386-pc --recheck /dev/nvme0n1 grub-install --target=i386-pc --recheck /dev/nvme1n1 update-grub fi ==== 14. User et SSH ==== $ passwd # root password setup $ adduser phil $ usermod -aG sudo phil # SSH key $ mkdir -p /home/phil/.ssh $ cat > /home/phil/.ssh/authorized_keys <<'EOF' ssh-ed25519 AAAA... phil@host EOF $ chmod 700 /home/phil/.ssh $ chmod 600 /home/phil/.ssh/authorized_keys $ chown -R phil:phil /home/phil/.ssh # sshd hardening $ cat > /etc/ssh/sshd_config.d/10-hardening.conf < ==== 15. Dropbear-initramfs ==== $ apt install -y dropbear-initramfs busybox # Config dropbear : port 4222 and force cryptroot-unlock command $ cat > /etc/dropbear/initramfs/dropbear.conf <<'EOF' DROPBEAR_OPTIONS="-p 4222 -s -j -k -I 60 -c cryptroot-unlock" EOF # ssh key allowed to connect $ cat > /etc/dropbear/initramfs/authorized_keys <<'EOF' ssh-ed25519 AAAA... phil@host EOF $ chmod 600 /etc/dropbear/initramfs/authorized_keys # network config for initramfs cat >> /etc/initramfs-tools/initramfs.conf < ==== 16. reboot ==== $ exit # exit chroot # $MODE from section 1 is still set in the rescue shell here $ if [ "$MODE" = uefi ]; then umount /mnt/sys/firmware/efi/efivars umount /mnt/boot/efi fi $ umount /mnt/boot $ umount /mnt/run $ umount /mnt/sys $ umount /mnt/proc $ umount /mnt/dev/pts $ umount /mnt/dev $ umount /mnt $ vgchange -an vg0 $ cryptsetup close cryptroot $ mdadm --stop /dev/md0 /dev/md1 $ reboot ==== 17. First boot and unlock ==== # Wait dropbear to be up nc -zv xx.xx.xx.xx 4222 # remote unlocking oneliner: pass show luks/schwartz | tr -d '\n' | ssh -p 4222 root@xx.xx.xx.xx cryptroot-unlock After few seconds, normal SSD become available. ==== 18. (EXTRA) hugepage & swap & LVM thin configuration ==== sudo lvcreate -L 4G -n swap vg0 sudo mkswap /dev/vg0/swap sudo swapon /dev/vg0/swap echo "/dev/vg0/swap none swap sw 0 0" | sudo tee -a /etc/fstab If you want to configure hugepage for a qemu hypervisor: sudo tee /etc/sysctl.d/10-hugepages.conf > /dev/null <<'EOF' vm.nr_hugepages = 13312 # EXAMPLE FOR 26GB/32GB EOF LVM Thin initialization to store VM storage sudo lvcreate --type thin-pool -L 250G -n thin vg0