diff --git a/install.sh b/install.sh index 7590bcc..296bf6a 100755 --- a/install.sh +++ b/install.sh @@ -88,140 +88,42 @@ echo "Please save these credentials securely. You will need them for recovery." echo "Press Enter to continue..." read -### End of user interaction - -# Install required package +# Install required packages 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 echo "[+] Detecting disk configuration..." 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[@]}" +if [ ${#DISKS[@]} -lt 1 ]; then + echo "Error: Expected at least 1 disk, found ${#DISKS[@]}" exit 1 fi -# Set disk variables -DISK1="/dev/${DISKS[0]}" -DISK2="/dev/${DISKS[1]}" +DISK="/dev/${DISKS[0]}" -# Stop any existing RAID arrays -echo "[+] Stopping any existing RAID arrays..." -mdadm --stop /dev/md0 2>/dev/null || true - -# Unmount any existing partitions -echo "[+] Unmounting partitions..." -for disk in $DISK1 $DISK2; do - umount -f $disk* 2>/dev/null || true -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 +# Create a small partition for installer files +echo "[+] Creating installer partition..." +parted -s $DISK mklabel gpt +parted -s $DISK mkpart primary ext4 0% 2GB +parted -s $DISK name 1 installer +mkfs.ext4 -L installer ${DISK}1 +mkdir -p /mnt/installer +mount ${DISK}1 /mnt/installer # Download 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 echo "[+] Mounting Fedora installer..." 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 echo "[+] Detecting current IP address and gateway..." 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}') echo "[+] Detected network configuration:" @@ -231,7 +133,7 @@ echo "Gateway: $GATEWAY" # Create kickstart file echo "[+] Creating kickstart configuration..." -cat > /mnt/ks.cfg << KICKSTART +cat > /mnt/installer/ks.cfg << 'KICKSTART' # Fedora Server installation with our secure setup text lang en_US.UTF-8 @@ -286,6 +188,153 @@ xclip python3-pip %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 # Configure network with static IP (Hetzner dedicated server style) @@ -293,16 +342,16 @@ cat > /etc/sysconfig/network-scripts/ifcfg-ens3 << EOF DEVICE=ens3 ONBOOT=yes BOOTPROTO=static -IPADDR=${IPV4} +IPADDR=$IPV4 NETMASK=255.255.255.255 -SCOPE="peer ${GATEWAY}" +SCOPE="peer $GATEWAY" EOF # Create route file cat > /etc/sysconfig/network-scripts/route-ens3 << EOF ADDRESS0=0.0.0.0 NETMASK0=0.0.0.0 -GATEWAY0=${GATEWAY} +GATEWAY0=$GATEWAY EOF # Reload network configuration @@ -327,7 +376,7 @@ DROPBEAR # Add SSH key to 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 # Regenerate initramfs with dropbear @@ -336,196 +385,29 @@ dracut -f # Set up MOTD if [ "$ENABLE_MOTD" = true ]; then cat > /etc/motd << "MOTD" - :^7J5GB##&&##GPY?~: - ^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] +$BANNER MOTD 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 systemctl enable clevis-luks-askpass 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 touch /.autorelabel - -# Cleanup -echo "[+] Now rebooting..." -reboot +%end 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 -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 -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" -set timeout=5 -set default=0 - -menuentry "Fedora Installer" { - 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} - initrd /installer/initrd.img -} -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 \ No newline at end of file +echo "[+] Configuration complete. Starting installation with kexec..." +echo "----------------------------------------" +cat /mnt/installer/ks.cfg +echo "----------------------------------------" +read -p "Press Enter to continue..." +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}" +kexec -e \ No newline at end of file