installer does more

This commit is contained in:
Dominik Moritz Roth 2025-05-19 16:54:46 +02:00
parent 8a39511aea
commit ce1736a7c1

View File

@ -88,140 +88,42 @@ echo "Please save these credentials securely. You will need them for recovery."
echo "Press Enter to continue..." echo "Press Enter to continue..."
read read
### End of user interaction # Install required packages
# Install required package
apt-get update apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y genisoimage grub-efi-amd64-bin util-linux DEBIAN_FRONTEND=noninteractive apt-get install -y genisoimage grub-efi-amd64-bin util-linux kexec-tools
# Detect disk naming scheme and set variables # Detect disk naming scheme and set variables
echo "[+] Detecting disk configuration..." echo "[+] Detecting disk configuration..."
DISKS=($(lsblk -d -n -o NAME | grep -E '^(sd[a-z]|nvme[0-9]+n[0-9]+)$' | sort)) DISKS=($(lsblk -d -n -o NAME | grep -E '^(sd[a-z]|nvme[0-9]+n[0-9]+)$' | sort))
if [ ${#DISKS[@]} -ne 2 ]; then if [ ${#DISKS[@]} -lt 1 ]; then
echo "Error: Expected exactly 2 disks, found ${#DISKS[@]}" echo "Error: Expected at least 1 disk, found ${#DISKS[@]}"
exit 1 exit 1
fi fi
# Set disk variables DISK="/dev/${DISKS[0]}"
DISK1="/dev/${DISKS[0]}"
DISK2="/dev/${DISKS[1]}"
# Stop any existing RAID arrays # Create a small partition for installer files
echo "[+] Stopping any existing RAID arrays..." echo "[+] Creating installer partition..."
mdadm --stop /dev/md0 2>/dev/null || true parted -s $DISK mklabel gpt
parted -s $DISK mkpart primary ext4 0% 2GB
# Unmount any existing partitions parted -s $DISK name 1 installer
echo "[+] Unmounting partitions..." mkfs.ext4 -L installer ${DISK}1
for disk in $DISK1 $DISK2; do mkdir -p /mnt/installer
umount -f $disk* 2>/dev/null || true mount ${DISK}1 /mnt/installer
done
# Stop any device mapper devices
echo "[+] Stopping device mapper devices..."
dmsetup remove_all 2>/dev/null || true
# Disconnect NVMe devices if present
if [[ "$DISK1" =~ nvme ]] || [[ "$DISK2" =~ nvme ]]; then
echo "Disconnecting NVMe devices..."
nvme disconnect-all
sleep 2
fi
# Zero out partition tables
echo "[+] Zeroing out partition tables..."
for disk in $DISK1 $DISK2; do
# Use blkdiscard for NVMe drives, dd for others
if [[ "$disk" =~ nvme ]]; then
blkdiscard -f $disk
else
# Zero first 2MB and last 2MB of disk
dd if=/dev/zero of=$disk bs=1M count=2 conv=fsync
dd if=/dev/zero of=$disk bs=1M seek=$(($(blockdev --getsz $disk) / 2048 - 2)) count=2 conv=fsync
fi
sync
done
# Create partitions
echo "[+] Creating partitions..."
for disk in $DISK1 $DISK2; do
# Create new GPT partition table
parted -s $disk mklabel gpt
# Create EFI partition (512MB)
parted -s $disk mkpart primary fat32 0% 512MB
# Create boot partition (1GB)
parted -s $disk mkpart primary ext4 512MB 1.5GB
# Create root partition (rest of disk)
parted -s $disk mkpart primary ext4 1.5GB 100%
# Set boot flag on first partition
parted -s $disk set 1 boot on
sync
done
# For NVMe disks, we need to append 'p' to partition numbers
if [[ "$DISK1" =~ nvme ]]; then
PART1="${DISK1}p1"
PART2="${DISK1}p2"
PART3="${DISK1}p3"
PART4="${DISK2}p1"
PART5="${DISK2}p2"
PART6="${DISK2}p3"
else
PART1="${DISK1}1"
PART2="${DISK1}2"
PART3="${DISK1}3"
PART4="${DISK2}1"
PART5="${DISK2}2"
PART6="${DISK2}3"
fi
echo "Detected disks:"
echo "Disk 1: $DISK1 (will make partitions: $PART1, $PART2, $PART3)"
echo "Disk 2: $DISK2 (will make partitions: $PART4, $PART5, $PART6)"
# Create EFI partitions
echo "[+] Creating EFI partitions..."
mkfs.vfat -F 32 $PART1
mkfs.vfat -F 32 $PART4
# Create boot RAID1
echo "[+] Creating boot RAID1 array..."
mdadm --create /dev/md0 --level=1 --raid-devices=2 --metadata=0.90 --force --run $PART2 $PART5
mkfs.ext4 /dev/md0
# Create LUKS volumes
echo "[+] Setting up LUKS encryption..."
echo "${LUKS_PASSPHRASE}" | cryptsetup luksFormat $PART3 --type luks2
echo "${LUKS_PASSPHRASE}" | cryptsetup luksFormat $PART6 --type luks2
# Open LUKS volumes
echo "[+] Opening LUKS volumes..."
echo "${LUKS_PASSPHRASE}" | cryptsetup luksOpen $PART3 root_a
echo "${LUKS_PASSPHRASE}" | cryptsetup luksOpen $PART6 root_b
# Create BTRFS RAID1
echo "[+] Creating BTRFS RAID1 filesystem..."
mkfs.btrfs -f -d raid1 -m raid1 /dev/mapper/root_a /dev/mapper/root_b
# Create subvolumes
echo "[+] Creating BTRFS subvolumes..."
mount /dev/mapper/root_a /mnt
btrfs subvolume create /mnt/@root
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@db
chattr +C /mnt/@db
# Download Fedora installer # Download Fedora installer
echo "[+] Downloading Fedora installer..." echo "[+] Downloading Fedora installer..."
wget "https://download.fedoraproject.org/pub/fedora/linux/releases/${FEDORA_VERSION}/Server/x86_64/iso/Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso" wget -O /mnt/installer/Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso "https://download.fedoraproject.org/pub/fedora/linux/releases/${FEDORA_VERSION}/Server/x86_64/iso/Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso"
# Mount Fedora ISO # Mount Fedora ISO
echo "[+] Mounting Fedora installer..." echo "[+] Mounting Fedora installer..."
mkdir -p /mnt/iso mkdir -p /mnt/iso
mount -o loop "Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso" /mnt/iso mount -o loop /mnt/installer/Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso /mnt/iso
# Get current IP address and gateway from first non-loopback interface # Get current IP address and gateway from first non-loopback interface
echo "[+] Detecting current IP address and gateway..." echo "[+] Detecting current IP address and gateway..."
INTERFACE=$(ip -o -4 route show to default | awk '{print $5}' | head -n1) INTERFACE=$(ip -o -4 route show to default | awk '{print $5}' | head -n1)
IPV4=$(ip -4 addr show $INTERFACE | grep -oP '(?<=inet\s)\d+(\.\d+){3}') IPV4=$(ip -4 addr show $INTERFACE | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}')
GATEWAY=$(ip route show default | awk '/default/ {print $3}') GATEWAY=$(ip route show default | awk '/default/ {print $3}')
echo "[+] Detected network configuration:" echo "[+] Detected network configuration:"
@ -231,7 +133,7 @@ echo "Gateway: $GATEWAY"
# Create kickstart file # Create kickstart file
echo "[+] Creating kickstart configuration..." echo "[+] Creating kickstart configuration..."
cat > /mnt/ks.cfg << KICKSTART cat > /mnt/installer/ks.cfg << 'KICKSTART'
# Fedora Server installation with our secure setup # Fedora Server installation with our secure setup
text text
lang en_US.UTF-8 lang en_US.UTF-8
@ -286,6 +188,153 @@ xclip
python3-pip python3-pip
%end %end
# Pre-installation
%pre
#!/bin/bash
# Use the LUKS passphrase from rescue script
LUKS_PASSPHRASE="${LUKS_PASSPHRASE}"
echo "\$LUKS_PASSPHRASE" > /tmp/luks.key
chmod 600 /tmp/luks.key
# Detect disk naming scheme and set variables
DISKS=($(lsblk -d -n -o NAME | grep -E '^(sd[a-z]|nvme[0-9]+n[0-9]+)$' | sort))
if [ ${#DISKS[@]} -ne 2 ]; then
echo "Error: Expected exactly 2 disks, found ${#DISKS[@]}"
exit 1
fi
# Set disk variables
DISK1="/dev/${DISKS[0]}"
DISK2="/dev/${DISKS[1]}"
# Stop any existing RAID arrays
mdadm --stop /dev/md0 2>/dev/null || true
# Unmount any existing partitions
for disk in $DISK1 $DISK2; do
umount -f $disk* 2>/dev/null || true
done
# Stop any device mapper devices
dmsetup remove_all 2>/dev/null || true
# Disconnect NVMe devices if present
if [[ "$DISK1" =~ nvme ]] || [[ "$DISK2" =~ nvme ]]; then
nvme disconnect-all
sleep 2
fi
# Zero out partition tables
for disk in $DISK1 $DISK2; do
if [[ "$disk" =~ nvme ]]; then
blkdiscard -f $disk
else
dd if=/dev/zero of=$disk bs=1M count=2 conv=fsync
dd if=/dev/zero of=$disk bs=1M seek=$(($(blockdev --getsz $disk) / 2048 - 2)) count=2 conv=fsync
fi
sync
done
# Create partitions
for disk in $DISK1 $DISK2; do
parted -s $disk mklabel gpt
parted -s $disk mkpart primary fat32 0% 512MB
parted -s $disk mkpart primary ext4 512MB 1.5GB
parted -s $disk mkpart primary ext4 1.5GB 100%
parted -s $disk set 1 boot on
sync
done
# For NVMe disks, we need to append 'p' to partition numbers
if [[ "$DISK1" =~ nvme ]]; then
PART1="${DISK1}p1"
PART2="${DISK1}p2"
PART3="${DISK1}p3"
PART4="${DISK2}p1"
PART5="${DISK2}p2"
PART6="${DISK2}p3"
else
PART1="${DISK1}1"
PART2="${DISK1}2"
PART3="${DISK1}3"
PART4="${DISK2}1"
PART5="${DISK2}2"
PART6="${DISK2}3"
fi
# Create EFI partitions
mkfs.vfat -F 32 $PART1
mkfs.vfat -F 32 $PART4
# Create boot RAID1
mdadm --create /dev/md0 --level=1 --raid-devices=2 --metadata=0.90 --force --run $PART2 $PART5
mkfs.ext4 /dev/md0
# Create LUKS volumes
echo "$LUKS_PASSPHRASE" | cryptsetup luksFormat $PART3 --type luks2
echo "$LUKS_PASSPHRASE" | cryptsetup luksFormat $PART6 --type luks2
# Open LUKS volumes
echo "$LUKS_PASSPHRASE" | cryptsetup luksOpen $PART3 root_a
echo "$LUKS_PASSPHRASE" | cryptsetup luksOpen $PART6 root_b
# Create BTRFS RAID1
mkfs.btrfs -f -d raid1 -m raid1 /dev/mapper/root_a /dev/mapper/root_b
# Create subvolumes
mount /dev/mapper/root_a /mnt/sysimage
btrfs subvolume create /mnt/sysimage/@root
btrfs subvolume create /mnt/sysimage/@home
btrfs subvolume create /mnt/sysimage/@db
chattr +C /mnt/sysimage/@db
# Configure Clevis
if [ ${#TANG_SERVERS[@]} -gt 0 ] || [ "$TPM_ENABLED" = true ]; then
mkdir -p /mnt/sysimage/etc/clevis
# Build Tang servers JSON array if we have any
if [ ${#TANG_SERVERS[@]} -gt 0 ]; then
TANG_JSON="["
for server in "${TANG_SERVERS[@]}"; do
read -r url thumbprint <<< "$server"
TANG_JSON+="{\"url\":\"$url\",\"thumbprint\":\"$thumbprint\"},"
done
TANG_JSON="${TANG_JSON%,}]" # Remove trailing comma and close array
fi
# Calculate t value based on enabled methods
T_VALUE=1
if [ ${#TANG_SERVERS[@]} -gt 0 ] && [ "$TPM_ENABLED" = true ]; then
T_VALUE=2
fi
# Create Clevis config
cat > /mnt/sysimage/etc/clevis/clevis.conf << EOF
{
"t": $T_VALUE,
"pins": {
$([ "$TPM_ENABLED" = true ] && echo "\"tpm2\": {
\"pcr_bank\": \"$TPM_PCR_BANK\",
\"pcr_ids\": \"$TPM_PCR_IDS\"
},")
$([ ${#TANG_SERVERS[@]} -gt 0 ] && echo "\"tang\": {
\"t\": 1,
\"tang\": $TANG_JSON
}")
}
}
EOF
# Bind LUKS volumes
clevis luks bind -d $PART3 sss -c /mnt/sysimage/etc/clevis/clevis.conf
clevis luks bind -d $PART6 sss -c /mnt/sysimage/etc/clevis/clevis.conf
fi
# Cleanup
umount /mnt/sysimage
%end
# Post-installation # Post-installation
%post %post
# Configure network with static IP (Hetzner dedicated server style) # Configure network with static IP (Hetzner dedicated server style)
@ -293,16 +342,16 @@ cat > /etc/sysconfig/network-scripts/ifcfg-ens3 << EOF
DEVICE=ens3 DEVICE=ens3
ONBOOT=yes ONBOOT=yes
BOOTPROTO=static BOOTPROTO=static
IPADDR=${IPV4} IPADDR=$IPV4
NETMASK=255.255.255.255 NETMASK=255.255.255.255
SCOPE="peer ${GATEWAY}" SCOPE="peer $GATEWAY"
EOF EOF
# Create route file # Create route file
cat > /etc/sysconfig/network-scripts/route-ens3 << EOF cat > /etc/sysconfig/network-scripts/route-ens3 << EOF
ADDRESS0=0.0.0.0 ADDRESS0=0.0.0.0
NETMASK0=0.0.0.0 NETMASK0=0.0.0.0
GATEWAY0=${GATEWAY} GATEWAY0=$GATEWAY
EOF EOF
# Reload network configuration # Reload network configuration
@ -327,7 +376,7 @@ DROPBEAR
# Add SSH key to dropbear # Add SSH key to dropbear
mkdir -p /etc/dropbear mkdir -p /etc/dropbear
echo "${SSH_KEY}" > /etc/dropbear/authorized_keys echo "$SSH_KEY" > /etc/dropbear/authorized_keys
chmod 600 /etc/dropbear/authorized_keys chmod 600 /etc/dropbear/authorized_keys
# Regenerate initramfs with dropbear # Regenerate initramfs with dropbear
@ -336,196 +385,29 @@ dracut -f
# Set up MOTD # Set up MOTD
if [ "$ENABLE_MOTD" = true ]; then if [ "$ENABLE_MOTD" = true ]; then
cat > /etc/motd << "MOTD" cat > /etc/motd << "MOTD"
:^7J5GB##&&##GPY?~: $BANNER
^75B&@@@@@@&&&@@@@@@@#GJ~:
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
Y##P7^ :~JB#B!
:: :
7PP?: :^~!!~^: :?PP7
:B@@B: !5B&@@@@&B5! :#@@B:
:!!: ^G@@@&BPPB@@@@G^ :!!:
:B@@@5^ ^5@@@B:
:7J7: !@@@# :&@@@~ :?J7:
J@@@5 :#@@@Y: :Y@@@B: 5@@@J
!@@@&^ ~B@@@&G55G&@@@B~ ~&@@@~
5@@@G: :7P#@@@@@@#P7: :B@@@Y
:P@@@B~ :~!77!~: ~B@@@P
Y@@@&Y^ ^5@@@@J
!G@@@&P7^ ^7P&@@@G~
!P&@@@&B? :: ?B&@@@&P!
^75#&&Y :P&&5: 5&&B57^
:^^ :P&&5: ^^:
^^
[nullpoint]
MOTD MOTD
fi fi
# Configure Clevis
if [ ${#TANG_SERVERS[@]} -gt 0 ] || [ "${TPM_ENABLED}" = true ]; then
mkdir -p /etc/clevis
# Build Tang servers JSON array if we have any
if [ ${#TANG_SERVERS[@]} -gt 0 ]; then
TANG_JSON="["
for server in "${TANG_SERVERS[@]}"; do
read -r url thumbprint <<< "\$server"
TANG_JSON+="{\"url\":\"\$url\",\"thumbprint\":\"\$thumbprint\"},"
done
TANG_JSON="\${TANG_JSON%,}]" # Remove trailing comma and close array
fi
# Create Clevis config
cat > /etc/clevis/clevis.conf << "CLEVIS"
{
"t": 2,
"pins": {
$([ "${TPM_ENABLED}" = true ] && echo "\"tpm2\": {
\"pcr_bank\": \"${TPM_PCR_BANK}\",
\"pcr_ids\": \"${TPM_PCR_IDS}\"
},")
$([ ${#TANG_SERVERS[@]} -gt 0 ] && echo "\"tang\": {
\"t\": 1,
\"tang\": \${TANG_JSON}
}")
}
}
CLEVIS
# Bind LUKS volumes
clevis luks bind -d ${PART3} sss -c /etc/clevis/clevis.conf
clevis luks bind -d ${PART6} sss -c /etc/clevis/clevis.conf
else
echo "No Tang servers or TPM available, skipping Clevis setup"
fi
# Enable required services # Enable required services
systemctl enable clevis-luks-askpass systemctl enable clevis-luks-askpass
systemctl enable dropbear systemctl enable dropbear
# Create TPM update script
cat > /root/update-tpm-bindings.py << "TPMSCRIPT"
#!/usr/bin/env python3
import subprocess
import sys
import os
def run_command(cmd):
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f"Error running command: {' '.join(cmd)}")
print(f"Error: {e.stderr}")
sys.exit(1)
def get_pcr_values():
print("Current PCR values:")
for i in range(0, 9):
pcr = run_command(['tpm2_pcrread', f'sha256:{i}'])
print(f"PCR {i}: {pcr.split('=')[1].strip()}")
def update_bindings():
print("\nUpdating TPM bindings...")
devices = ['${PART3}', '${PART6}']
for device in devices:
print(f"\nUpdating bindings for {device}")
try:
# Remove existing bindings
run_command(['clevis', 'luks', 'unbind', '-d', device, '-s', '1'])
run_command(['clevis', 'luks', 'unbind', '-d', device, '-s', '2'])
# Create new bindings
run_command(['clevis', 'luks', 'bind', '-d', device, 'sss', '-c', '/etc/clevis/clevis.conf'])
print(f"Successfully updated bindings for {device}")
except subprocess.CalledProcessError as e:
print(f"Error updating bindings for {device}: {e.stderr}")
continue
def verify_bindings():
print("\nVerifying bindings...")
devices = ['${PART3}', '${PART6}']
for device in devices:
print(f"\nBindings for {device}:")
try:
bindings = run_command(['clevis', 'luks', 'list', '-d', device])
print(bindings)
except subprocess.CalledProcessError as e:
print(f"Error verifying bindings for {device}: {e.stderr}")
continue
def main():
if os.geteuid() != 0:
print("This script must be run as root")
sys.exit(1)
print("TPM Binding Update Script")
print("========================")
get_pcr_values()
update_bindings()
verify_bindings()
print("\nUpdate complete. Please reboot to test the new bindings.")
if __name__ == "__main__":
main()
TPMSCRIPT
chmod +x /root/update-tpm-bindings.py
# Force SELinux relabel on next boot # Force SELinux relabel on next boot
touch /.autorelabel touch /.autorelabel
%end
# Cleanup
echo "[+] Now rebooting..."
reboot
KICKSTART KICKSTART
# Start Fedora installation
echo "Starting Fedora installation..."
echo "This will take some time. Please wait..."
echo "SSH access will be available on port 2222"
# Create ALL needed directories
mkdir -p /mnt/boot
mount /dev/md0 /mnt/boot
mkdir -p /mnt/boot/efi
mount $PART1 /mnt/boot/efi
mkdir -p /mnt/boot/efi/EFI/fedora
mkdir -p /mnt/boot/grub
mkdir -p /mnt/boot/installer
mkdir -p /mnt/iso
# Now mount and copy
cp /mnt/iso/images/pxeboot/vmlinuz /mnt/boot/installer/
cp /mnt/iso/images/pxeboot/initrd.img /mnt/boot/installer/
cp /mnt/ks.cfg /mnt/boot/installer/
# Get actual ISO label # Get actual ISO label
ISO_LABEL=$(isoinfo -d -i "Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso" | grep "Volume id:" | cut -d: -f2 | tr -d ' ') ISO_LABEL=$(isoinfo -d -i /mnt/installer/Fedora-Server-netinst-x86_64-${FEDORA_VERSION}-1.1.iso | grep "Volume id:" | cut -d: -f2 | tr -d ' ')
# Set IP-related kernel boot params for installer # Set IP-related kernel boot params for installer
KERNEL_NET_PARAMS="ip=${IPV4}::${GATEWAY}:255.255.255.255::ens3:none nameserver=185.12.64.1 nameserver=185.12.64.2" KERNEL_NET_PARAMS="ip=$IPV4::$GATEWAY:255.255.255.255::ens3:none nameserver=185.12.64.1 nameserver=185.12.64.2"
cat > /mnt/boot/grub/grub.cfg << "GRUBCFG" echo "[+] Configuration complete. Starting installation with kexec..."
set timeout=5 echo "----------------------------------------"
set default=0 cat /mnt/installer/ks.cfg
echo "----------------------------------------"
menuentry "Fedora Installer" { read -p "Press Enter to continue..."
linux /installer/vmlinuz inst.ks=file:///installer/ks.cfg inst.stage2=hd:LABEL=${ISO_LABEL} ${KERNEL_NET_PARAMS} inst.sshd inst.ssh.port=2222 inst.ssh.key=${SSH_KEY} kexec -l /mnt/iso/images/pxeboot/vmlinuz --initrd=/mnt/iso/images/pxeboot/initrd.img --append="inst.ks=file:///dev/disk/by-label/installer/ks.cfg inst.stage2=hd:LABEL=${ISO_LABEL} ${KERNEL_NET_PARAMS} inst.sshd inst.ssh.port=2222 inst.ssh.key=${SSH_KEY}"
initrd /installer/initrd.img kexec -e
}
GRUBCFG
# Install GRUB EFI
grub-install --target=x86_64-efi --boot-directory=/mnt/boot --efi-directory=/mnt/boot/efi --removable $DISK1
grub-install --target=x86_64-efi --boot-directory=/mnt/boot --efi-directory=/mnt/boot/efi --removable $DISK2
umount /mnt/boot/efi
umount /mnt/boot
umount /mnt/iso
#reboot