nullpoint/install.sh
2025-05-19 16:54:46 +02:00

413 lines
12 KiB
Bash
Executable File

#!/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