Installation script until stage3 extraction and chroot complete

This commit is contained in:
oddlama 2019-12-31 16:28:42 +01:00
commit 76bf36f20b
No known key found for this signature in database
GPG Key ID: 88EA325D51D53908
13 changed files with 615 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 oddlama
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

53
README.md Normal file
View File

@ -0,0 +1,53 @@
# Gentoo installation script
This script will install a minimal bootable gentoo system.
If you need advanced features such as an initramfs or a different
partitioning scheme, you can definitely use this script but will
have to make some adjustments to it.
The main purpose of this script is to provide a universal setup
which should be suitable for most use-cases (desktop and server installations).
#### Overview of executed tasks
* Check live system
* Sync time
* Partition disks
* Format partitions
* Download stage3
* Extract stage3
* Chroot into new system
* Update portage tree
* ... TODO MISSING!
#### GPT
The script will create GPT partition tables. If your system cannot use GPT,
this script is not suited for it.
#### EFI
It is assumed that your system can (and will) be booted via EFI.
This is not a strict requirement, but othewise you will be responsible
to make the system bootable.
This probably involves the following steps:
* Change partition type of `efi` partition to `ef02` (BIOS boot partition)
* Change partition name and filesystem name to `boot`
* Install and configure syslinux
Maybe there will be a convenience script for this at some point.
No promises though.
# Optional: Ansible ready
Optionally, this script can make the new system ready to be
used with ansible.
It will do the following steps for you:
* Create an ansible user
* Generate an ssh keypair (type configurable)
* Setup a secure sshd (safe ciphers, login only with keypair)
* Install ansible

1
chroot Symbolic link
View File

@ -0,0 +1 @@
scripts/main.sh

1
install Symbolic link
View File

@ -0,0 +1 @@
scripts/main.sh

1
install_gentoo Symbolic link
View File

@ -0,0 +1 @@
scripts/main.sh

1
install_stage3 Symbolic link
View File

@ -0,0 +1 @@
scripts/main.sh

44
scripts/config.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/bash
source "$GENTOO_BOOTSTRAP_DIR/scripts/protection.sh" || exit 1
source "$GENTOO_BOOTSTRAP_DIR/scripts/internal_config.sh" || exit 1
################################################
# Disk configuration
# Enable swap?
ENABLE_SWAP=false
# Enable partitioning (will still ask before doing anything critical)
ENABLE_PARTITIONING=true
# The device to partition
PARTITION_DEVICE="/dev/sda"
# Size of swap partition (if enabled)
PARTITION_SWAP_SIZE="8GiB"
# The size of the EFI partition
PARTITION_EFI_SIZE="128MiB"
# Partition UUIDs.
# You must insert these by hand, if you do not use automatic partitioning
PARTITION_UUID_EFI="$(load_or_generate_uuid 'efi')"
PARTITION_UUID_SWAP="$(load_or_generate_uuid 'swap')"
PARTITION_UUID_LINUX="$(load_or_generate_uuid 'linux')"
# Format the partitions with the correct filesystems,
# if you didn't chose automatic partitioning, you will be asked
# before any formatting is done.
ENABLE_FORMATTING=true
################################################
# Gentoo configuration
# The selected gentoo mirror
GENTOO_MIRROR="https://mirror.eu.oneandone.net/linux/distributions/gentoo/gentoo"
#GENTOO_MIRROR="https://distfiles.gentoo.org"
# The stage3 tarball to install
STAGE3_BASENAME="stage3-amd64-hardened+nomultilib"
#STAGE3_BASENAME="stage3-amd64-hardened-selinux+nomultilib"

278
scripts/functions.sh Normal file
View File

@ -0,0 +1,278 @@
#!/bin/bash
source "$GENTOO_BOOTSTRAP_DIR/scripts/protection.sh" || exit 1
################################################
# Functions
check_has_program() {
type "$1" &>/dev/null \
|| die "Missing program: '$1'"
}
sync_time() {
einfo "Syncing time"
ntpd -g -q >/dev/null \
|| die "Could not sync time with remote server"
einfo "Current date: $(LANG=C date)"
einfo "Writing time to hardware clock"
hwclock --systohc --utc >/dev/null \
|| die "Could not save time to hardware clock"
}
prepare_installation_environment() {
einfo "Preparing installation environment"
check_has_program gpg
check_has_program hwclock
check_has_program lsblk
check_has_program ntpd
check_has_program partprobe
check_has_program python3
check_has_program rhash
check_has_program sgdisk
check_has_program uuidgen
check_has_program wget
sync_time
}
partition_device_print_config_summary() {
echo "-------- Partition configuration --------"
echo "Device: $PARTITION_DEVICE"
elog "Existing partition table:"
lsblk -n "$PARTITION_DEVICE" \
|| die "Error in lsblk"
elog "New partition table:"
echo "$PARTITION_DEVICE"
echo "├─efi size=$PARTITION_EFI_SIZE"
if [[ "$ENABLE_SWAP" == true ]]; then
echo "├─swap size=$PARTITION_SWAP_SIZE"
fi
echo "└─linux size=[remaining]"
if [[ "$ENABLE_SWAP" != true ]]; then
echo "swap: disabled"
fi
echo
}
partition_device() {
[[ "$ENABLE_PARTITIONING" == true ]] \
|| return 0
einfo "Preparing partitioning of device '$PARTITION_DEVICE'"
[[ -b "$PARTITION_DEVICE" ]] \
|| die "Selected device '$PARTITION_DEVICE' is not a block device"
partition_device_print_config_summary
ask "Do you really want to apply this partitioning?" \
|| die "For manual partitioning formatting please set ENABLE_PARTITIONING=false in config.sh"
countdown "Partitioning in " 5
einfo "Partitioning device '$PARTITION_DEVICE'"
# Delete any existing partition table
sgdisk -Z "$PARTITION_DEVICE" >/dev/null \
|| die "Could not delete existing partition table"
# Create efi/boot partition
sgdisk -n "0:0:+$PARTITION_EFI_SIZE" -t 0:ef00 -c 0:"efi" -u 0:"$PARTITION_UUID_EFI" "$PARTITION_DEVICE" >/dev/null \
|| die "Could not create efi partition"
# Create swap partition
if [[ "$ENABLE_SWAP" == true ]]; then
sgdisk -n "0:0:+$PARTITION_SWAP_SIZE" -t 0:8200 -c 0:"swap" -u 0:"$PARTITION_UUID_SWAP" "$PARTITION_DEVICE" >/dev/null \
|| die "Could not create swap partition"
fi
# Create system partition
sgdisk -n 0:0:0 -t 0:8300 -c 0:"linux" -u 0:"$PARTITION_UUID_LINUX" "$PARTITION_DEVICE" >/dev/null \
|| die "Could not create linux partition"
# Print partition table
einfo "Applied partition table:"
sgdisk -p "$PARTITION_DEVICE" \
|| die "Could not print partition table"
# Inform kernel of partition table changes
partprobe "$PARTITION_DEVICE" \
|| die "Could not probe partitions"
}
format_partitions() {
[[ "$ENABLE_FORMATTING" == true ]] \
|| return 0
if [[ "$ENABLE_PARTITIONING" != true ]]; then
einfo "Preparing to format the following partitions:"
blkid -t PARTUUID="$PARTITION_UUID_EFI" \
|| die "Error while listing efi partition"
if [[ "$ENABLE_SWAP" == true ]]; then
blkid -t PARTUUID="$PARTITION_UUID_SWAP" \
|| die "Error while listing swap partition"
fi
blkid -t PARTUUID="$PARTITION_UUID_LINUX" \
|| die "Error while listing linux partition"
ask "Do you really want to format these partitions?" \
|| die "For manual formatting please set ENABLE_FORMATTING=false in config.sh"
countdown "Formatting in " 5
fi
einfo "Formatting partitions"
local dev
dev="$(get_device_by_partuuid "$PARTITION_UUID_EFI")" \
|| die "Could not resolve partition UUID '$PARTITION_UUID_EFI'"
einfo " $dev (efi)"
mkfs.fat -F 32 -n "efi" "$dev" \
|| die "Could not format EFI partition"
if [[ "$ENABLE_SWAP" == true ]]; then
dev="$(get_device_by_partuuid "$PARTITION_UUID_SWAP")" \
|| die "Could not resolve partition UUID '$PARTITION_UUID_SWAP'"
einfo " $dev (swap)"
mkswap -L "swap" "$dev" \
|| die "Could not create swap"
fi
dev="$(get_device_by_partuuid "$PARTITION_UUID_LINUX")" \
|| die "Could not resolve partition UUID '$PARTITION_UUID_LINUX'"
einfo " $dev (linux)"
mkfs.ext4 -L "linux" "$dev" \
|| die "Could not create ext4 filesystem"
}
mount_root() {
# Skip if root is already mounted
mountpoint -q -- "$ROOT_MOUNTPOINT" \
&& return
# Mount root device
einfo "Mounting root device"
mkdir -p "$ROOT_MOUNTPOINT" \
|| die "Could not create mountpoint directory $ROOT_MOUNTPOINT"
local dev
dev="$(get_device_by_partuuid "$PARTITION_UUID_LINUX")" \
|| die "Could not resolve partition UUID '$PARTITION_UUID_LINUX'"
mount "$dev" "$ROOT_MOUNTPOINT" \
|| die "Could not mount root device '$dev'"
}
bind_bootstrap_dir() {
# Bind the bootstrap dir to a location in /tmp,
# so it can be accessed from within the chroot
mountpoint -q -- "$GENTOO_BOOTSTRAP_BIND" \
&& return
# Mount root device
einfo "Bind mounting bootstrap directory"
mkdir -p "$GENTOO_BOOTSTRAP_BIND" \
|| die "Could not create mountpoint directory '$GENTOO_BOOTSTRAP_BIND'"
mount --bind "$GENTOO_BOOTSTRAP_DIR" "$GENTOO_BOOTSTRAP_BIND" \
|| die "Could not bind mount '$GENTOO_BOOTSTRAP_DIR' to '$GENTOO_BOOTSTRAP_BIND'"
}
download_stage3() {
cd "$TMP_DIR" \
|| die "Could not cd into '$TMP_DIR'"
local STAGE3_RELEASES="$GENTOO_MIRROR/releases/amd64/autobuilds/current-$STAGE3_BASENAME/"
# Download upstream list of files
CURRENT_STAGE3="$(download_stdout "$STAGE3_RELEASES")" \
|| die "Could not retrieve list of tarballs"
# Decode urlencoded strings
CURRENT_STAGE3=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read()))' <<< "$CURRENT_STAGE3")
# Parse output for correct filename
CURRENT_STAGE3="$(grep -o "\"${STAGE3_BASENAME}-[0-9A-Z]*.tar.xz\"" <<< "$CURRENT_STAGE3" \
| sort -u | head -1)" \
|| die "Could not parse list of tarballs"
# Strip quotes
CURRENT_STAGE3="${CURRENT_STAGE3:1:-1}"
# Download file if not already downloaded
if [[ -e "$CURRENT_STAGE3" ]] ; then
einfo "$STAGE3_BASENAME tarball already exists"
else
einfo "Downloading $STAGE3_BASENAME tarball"
download "$STAGE3_RELEASES/${CURRENT_STAGE3}" "${CURRENT_STAGE3}"
download "$STAGE3_RELEASES/${CURRENT_STAGE3}.DIGESTS.asc" "${CURRENT_STAGE3}.DIGESTS.asc"
fi
# Import gentoo keys
einfo "Importing gentoo gpg key"
local GENTOO_GPG_KEY="$TMP_DIR/gentoo-keys.gpg"
download "https://gentoo.org/.well-known/openpgpkey/hu/wtktzo4gyuhzu8a4z5fdj3fgmr1u6tob?l=releng" "$GENTOO_GPG_KEY" \
|| die "Could not retrieve gentoo gpg key"
gpg --import < "$GENTOO_GPG_KEY" \
|| die "Could not import gentoo gpg key"
# Verify DIGESTS signature
einfo "Verifying DIGEST.asc signature"
gpg --verify "${CURRENT_STAGE3}.DIGESTS.asc" \
|| die "Signature of '${CURRENT_STAGE3}.DIGESTS.asc' invalid!"
# Check hashes
einfo "Verifying tarball integrity"
rhash -P --check <(grep -B 1 'tar.xz$' "${CURRENT_STAGE3}.DIGESTS.asc") \
|| die "Checksum mismatch!"
}
extract_stage3() {
mount_root
[[ -n $CURRENT_STAGE3 ]] \
|| die "CURRENT_STAGE3 is not set"
[[ -e "$TMP_DIR/$CURRENT_STAGE3" ]] \
|| die "stage3 file does not exist"
# Go to root directory
cd "$ROOT_MOUNTPOINT" \
|| die "Could not move to '$ROOT_MOUNTPOINT'"
# Ensure the directory is empty
find . -mindepth 1 -maxdepth 1 -not -name 'lost+found' \
| grep -q . \
&& die "root directory '$ROOT_MOUNTPOINT' is not empty"
# Extract tarball
einfo "Extracting stage3 tarball"
tar xpf "$TMP_DIR/$CURRENT_STAGE3" --xattrs --numeric-owner \
|| die "Error while extracting tarball"
cd "$TMP_DIR" \
|| die "Could not cd into '$TMP_DIR'"
}
gentoo_chroot() {
[[ $# -gt 0 ]] || die "Missing command argument"
mount_root
bind_bootstrap_dir
# Copy resolv.conf
einfo "Preparing chroot environment"
cp /etc/resolv.conf "$ROOT_MOUNTPOINT/etc/resolv.conf" \
|| die "Could not copy resolv.conf"
# Mount virtual filesystems
einfo "Mounting virtual filesystems"
(
mountpoint -q -- "$ROOT_MOUNTPOINT/proc" || mount -t proc /proc "$ROOT_MOUNTPOINT/proc" || exit 1
mountpoint -q -- "$ROOT_MOUNTPOINT/tmp" || mount --rbind /tmp "$ROOT_MOUNTPOINT/tmp" || exit 1
mountpoint -q -- "$ROOT_MOUNTPOINT/sys" || mount --rbind /sys "$ROOT_MOUNTPOINT/sys" || exit 1
mountpoint -q -- "$ROOT_MOUNTPOINT/sys" || mount --make-rslave "$ROOT_MOUNTPOINT/sys" || exit 1
mountpoint -q -- "$ROOT_MOUNTPOINT/dev" || mount --rbind /dev "$ROOT_MOUNTPOINT/dev" || exit 1
mountpoint -q -- "$ROOT_MOUNTPOINT/dev" || mount --make-rslave "$ROOT_MOUNTPOINT/dev" || exit 1
) || die "Could not mount virtual filesystems"
# Execute command
einfo "Chrooting..."
EXECUTED_IN_CHROOT=true \
TMP_DIR=$TMP_DIR \
exec chroot "$ROOT_MOUNTPOINT" "$GENTOO_BOOTSTRAP_BIND/scripts/main_chroot.sh" "$@" \
|| die "Failed to chroot into '$ROOT_MOUNTPOINT'"
}

View File

@ -0,0 +1,20 @@
#!/bin/bash
source "$GENTOO_BOOTSTRAP_DIR/scripts/protection.sh" || exit 1
################################################
# Script internal configuration
# The temporary directory for this script,
# must reside in /tmp to allow the chrooted system to access the files
TMP_DIR="/tmp/gentoo-bootstrap"
# Mountpoint for the new system
ROOT_MOUNTPOINT="$TMP_DIR/root"
# Mountpoint for the script files for access from chroot
GENTOO_BOOTSTRAP_BIND="$TMP_DIR/bind"
# Mountpoint for the script files for access from chroot
UUID_STORAGE_DIR="$TMP_DIR/uuids"
# The desired efi partition mountpoint for the actual system
EFI_MOUNTPOINT="/boot/efi"

94
scripts/main.sh Executable file
View File

@ -0,0 +1,94 @@
################################################
# Initialize script environment
# Find the directory this script is stored in. (from: http://stackoverflow.com/questions/59895)
get_source_dir() {
local source="${BASH_SOURCE[0]}"
while [[ -h "${source}" ]]
do
local tmp="$(cd -P "$(dirname "${source}")" && pwd)"
source="$(readlink "${source}")"
[[ "${source}" != /* ]] && source="${tmp}/${source}"
done
echo -n "$(realpath "$(dirname "${source}")")"
}
export GENTOO_BOOTSTRAP_DIR="$(dirname "$(get_source_dir)")"
export GENTOO_BOOTSTRAP_SCRIPT_ACTIVE=true
export GENTOO_BOOTSTRAP_SCRIPT_PID=$$
umask 0077
source "$GENTOO_BOOTSTRAP_DIR/scripts/utils.sh"
source "$GENTOO_BOOTSTRAP_DIR/scripts/config.sh"
source "$GENTOO_BOOTSTRAP_DIR/scripts/functions.sh"
mkdir -p "$TMP_DIR"
[[ $EUID == 0 ]] \
|| die "Must be root"
################################################
# Functions
main_install_stage3() {
[[ $# == 0 ]] || die "Too many arguments"
prepare_installation_environment
partition_device
format_partitions
download_stage3
extract_stage3
}
main_chroot() {
gentoo_chroot "$@"
}
main_install_gentoo() {
[[ $# == 0 ]] || die "Too many arguments"
#remove root password
passwd -d root
#get kernel
#compile minimal kernel to boot system
#reboot?
#mount boot partition
#create kernel
#create_ansible_user
#generate_fresh keys to become mgmnt ansible user
#install_ansible
einfo "Gentoo installation complete"
einfo "Dropping into chrooted shell"
su
}
main_install_full() {
[[ $# == 0 ]] || die "Too many arguments"
"$GENTOO_BOOTSTRAP_DIR/install_stage3" \
|| die "Failed to install stage3"
"$GENTOO_BOOTSTRAP_DIR/chroot" "$GENTOO_BOOTSTRAP_DIR/install_gentoo" \
|| die "Failed to prepare gentoo in chroot"
}
################################################
# Main dispatch
SCRIPT_ALIAS="$(basename "$0")"
case "$SCRIPT_ALIAS" in
"chroot") main_chroot "$@" ;;
"install") main_install_full "$@" ;;
"install_gentoo") main_install_gentoo "$@" ;;
"install_stage3") main_install_stage3 "$@" ;;
*) die "Invalid alias '$SCRIPT_ALIAS' was used to execute this script" ;;
esac

11
scripts/main_chroot.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
[[ "${EXECUTED_IN_CHROOT}" != true ]] \
&& { echo "This script must not be executed directly!" >&2; exit 1; }
source /etc/profile
export NPROC="$(($(nproc || echo 2) + 1))"
hostname 'gentoo'
exec "$@"

6
scripts/protection.sh Normal file
View File

@ -0,0 +1,6 @@
#!/bin/bash
if [[ "$GENTOO_BOOTSTRAP_SCRIPT_ACTIVE" != true ]]; then
echo " * ERROR: This script must not be executed directly!" >&2
exit 1
fi

84
scripts/utils.sh Normal file
View File

@ -0,0 +1,84 @@
#!/bin/bash
source "$GENTOO_BOOTSTRAP_DIR/scripts/protection.sh" || exit 1
elog() {
echo " * $*"
}
einfo() {
echo " * $*"
}
ewarn() {
echo " * $*" >&2
}
eerror() {
echo " * ERROR: $*" >&2
}
die() {
eerror "$*"
kill "$GENTOO_BOOTSTRAP_SCRIPT_PID"
exit 1
}
ask() {
while true; do
read -r -p "$* (Y/n) " response
case "${response,,}" in
'') return 0 ;;
y|yes) return 0 ;;
n|no) return 1 ;;
*) continue ;;
esac
done
}
countdown() {
echo -n "$1"
local i="$2"
while [[ $i -gt 0 ]]; do
echo -n "$i "
i=$((i - 1))
sleep 1
done
echo
}
download_stdout() {
wget --quiet --https-only --secure-protocol=PFS -O - -- "$1"
}
download() {
wget --quiet --https-only --secure-protocol=PFS --show-progress -O "$2" -- "$1"
}
get_device_by_partuuid() {
blkid -g \
|| die "Error while executing blkid"
local dev
dev="$(blkid -o export -t PARTUUID="$1")" \
|| die "Error while executing blkid to find PARTUUID=$1"
dev="$(grep DEVNAME <<< "$dev")" \
|| die "Could not find DEVNAME=... in blkid output"
dev="${dev:8}"
echo -n "$dev"
}
load_or_generate_uuid() {
local uuid
local uuid_file="$UUID_STORAGE_DIR/$1"
if [[ -e "$uuid_file" ]]; then
uuid="$(cat "$uuid_file")"
else
uuid="$(uuidgen -r)"
mkdir -p "$UUID_STORAGE_DIR"
echo -n "$uuid" > "$uuid_file"
fi
echo -n "$uuid"
}