#!/bin/bash BANNER=$(cat << "EOF" :^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] EOF ) TANG_SERVERS=( # "https://tang1.example.com your-thumbprint-1" # "https://tang2.example.com your-thumbprint-2" ) TPM_PCR_BANK="sha256" TPM_PCR_IDS="0,1,2,3,4,5,6,7,8" FEDORA_VERSION="42" FEDORA_USER="null" ENABLE_MOTD=true SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkoTn2NreAXMriOUqzyj3YoFW6jMo9B5B+3R5k8yrMi dodox@dodox-ProArt" ######################################################## # Config End ######################################################## set -euo pipefail alias c="clear" alias cls="clear" clear echo -e "\n$BANNER" echo -e "\n[+] Starting installation..." # Check for TPM echo "[+] Checking for TPM..." if [ ! -d "/sys/class/tpm/tpm0" ]; then echo "WARNING: No TPM detected!" echo "This system will not be able to use TPM-based boot verification." echo "You might need to enable TPM in the BIOS. (On Hetzner make a support ticket for KVM access)" read -p "Continue without TPM? [y/N] " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Installation aborted." exit 1 fi echo "Proceeding without TPM..." TPM_ENABLED=false else echo "TPM detected." TPM_ENABLED=true fi # Check for SSH key if [ -z "${SSH_KEY:-}" ]; then echo "No SSH key provided. Please enter your public SSH key:" read -r SSH_KEY if [ -z "$SSH_KEY" ]; then echo "Error: SSH key is required for installation" exit 1 fi fi # Generate secure LUKS passphrase echo "[+] Generating secure LUKS passphrase..." LUKS_PASSPHRASE=$(openssl rand -base64 30) echo "----------------------------------------" echo "Generated LUKS passphrase:" echo "${LUKS_PASSPHRASE}" echo "----------------------------------------" echo "Please save these credentials securely. You will need them for recovery." echo "Press Enter to continue..." read # Install required packages apt-get update 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[@]} -lt 1 ]; then echo "Error: Expected at least 1 disk, found ${#DISKS[@]}" exit 1 fi DISK="/dev/${DISKS[0]}" # 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 -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 /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}') GATEWAY=$(ip route show default | awk '/default/ {print $3}') echo "[+] Detected network configuration:" echo "Interface: $INTERFACE" echo "IP: $IPV4" echo "Gateway: $GATEWAY" # Create kickstart file echo "[+] Creating kickstart configuration..." cat > /mnt/installer/ks.cfg << 'KICKSTART' # Fedora Server installation with our secure setup text lang en_US.UTF-8 keyboard us timezone --utc Etc/UTC # Security settings selinux --enforcing rootpw --lock user --name=${FEDORA_USER} --groups=wheel --shell=/bin/bash --lock # SSH setup sshkey --username=${FEDORA_USER} "${SSH_KEY}" # Network - let installer detect interface network --bootproto=static --ip=${IPV4} --netmask=255.255.255.255 --gateway=${GATEWAY} --nameserver=185.12.64.1 --nameserver=185.12.64.2 --activate # Bootloader bootloader --timeout=1 --location=mbr --append="no_timer_check console=tty1 console=ttyS0,115200n8" # Services services --enabled=sshd,clevis-luks-askpass,dropbear # Use existing partitions part /boot/efi --fstype=vfat --onpart=${PART1} part /boot --fstype=ext4 --onpart=/dev/md0 part / --fstype=btrfs --onpart=/dev/mapper/root_a # Packages %packages @^server-product-environment clevis clevis-luks clevis-tang clevis-tpm2 tpm2-tools tpm2-tss cryptsetup btrfs-progs mdadm dropbear git zsh lsd bat tmux neovim fortune-mod cowsay lolcat 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) cat > /etc/sysconfig/network-scripts/ifcfg-ens3 << EOF DEVICE=ens3 ONBOOT=yes BOOTPROTO=static IPADDR=$IPV4 NETMASK=255.255.255.255 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 EOF # Reload network configuration nmcli con reload || true nmcli con up ens3 || true # Update fstab cat > /etc/fstab << "FSTAB" ${PART1} /boot/efi vfat defaults 1 2 /dev/md0 /boot ext4 defaults 1 2 /dev/mapper/root_a / btrfs subvol=@root,defaults,noatime 0 0 /dev/mapper/root_a /home btrfs subvol=@home,defaults,noatime 0 0 /dev/mapper/root_a /db btrfs subvol=@db,defaults,noatime,nodatacow 0 0 FSTAB # Configure dropbear for early SSH access mkdir -p /etc/dracut.conf.d cat > /etc/dracut.conf.d/dropbear.conf << "DROPBEAR" add_drivers+=" dropbear " install_optional_items=yes DROPBEAR # Add SSH key to dropbear mkdir -p /etc/dropbear echo "$SSH_KEY" > /etc/dropbear/authorized_keys chmod 600 /etc/dropbear/authorized_keys # Regenerate initramfs with dropbear dracut -f # Set up MOTD if [ "$ENABLE_MOTD" = true ]; then cat > /etc/motd << "MOTD" $BANNER MOTD fi # Enable required services systemctl enable clevis-luks-askpass systemctl enable dropbear # Force SELinux relabel on next boot touch /.autorelabel %end KICKSTART # Get actual ISO label 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" 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