aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Edgecumbe <git@esotericnonsense.com>2019-11-11 04:11:28 +0000
committerDaniel Edgecumbe <git@esotericnonsense.com>2019-11-11 04:11:28 +0000
commitc5b58c03efa403df3d1696aeb629e0ce47a5af52 (patch)
tree98963296f1f2f9d786e508d1d40f29d72ee0b0c5
Initial commit
-rw-r--r--README.md17
-rw-r--r--build/isolinux/isolinux.cfg6
-rwxr-xr-xbuild/main.sh188
-rw-r--r--build/syslinux/syslinux.cfg7
-rw-r--r--install/config.sh42
-rwxr-xr-xinstall/main.sh22
-rw-r--r--install/packages.list33
-rw-r--r--install/stage1.sh46
-rwxr-xr-xinstall/stage2.sh109
-rwxr-xr-xinstall/stage3.sh20
10 files changed, 490 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f1b025
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+---
+title: autoarch README.md
+---
+# autoarch
+
+This is a basic script, adapted from
+
+[archlinux/archiso.git](https://git.archlinux.org/archiso.git)
+
+to perform an automated install of archlinux in a virtual machine.
+
+## Important note
+
+The created ISO will _completely erase_ the contents of the disk you point it
+at (e.g. `vda` by default) without confirmation prior to installation.
+
+Consider yourself warned.
diff --git a/build/isolinux/isolinux.cfg b/build/isolinux/isolinux.cfg
new file mode 100644
index 0000000..33e8b65
--- /dev/null
+++ b/build/isolinux/isolinux.cfg
@@ -0,0 +1,6 @@
+PATH /%INSTALL_DIR%/boot/syslinux/
+DEFAULT loadconfig
+
+LABEL loadconfig
+ CONFIG /%INSTALL_DIR%/boot/syslinux/syslinux.cfg
+ APPEND /%INSTALL_DIR%/
diff --git a/build/main.sh b/build/main.sh
new file mode 100755
index 0000000..0ca97b7
--- /dev/null
+++ b/build/main.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+script_path=$(dirname "$(readlink -f "${0}")")
+
+iso_name=autoarch
+iso_label=autoarch_$(git rev-parse HEAD --git-dir="${script_path}" | head -c 8)
+iso_version=$(git rev-parse HEAD --git-dir="${script_path}" | head -c 8)
+install_dir=arch
+arch=$(uname -m)
+work_dir=/tmp/archiso-work
+out_dir=/tmp/archiso-out
+
+umask 0022
+
+# Helper function to run make_*() only one time per architecture.
+run_once() {
+ mkdir -p "${work_dir}"
+ if [[ ! -e ${work_dir}/build.${1}_${arch} ]]; then
+ local logfile="${work_dir}/build.${1}_${arch}"
+ echo "running $1... (see ${logfile})"
+ $1 > "${logfile}.stdout" 2> "${logfile}.stderr"
+ fi
+}
+
+# Base installation (airootfs)
+make_basefs() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" init
+}
+
+# Install necessary dependencies that are no longer in the base group
+make_install_kernel() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -p linux install
+}
+
+# Copy mkinitcpio archiso hooks and build initramfs (airootfs)
+make_setup_mkinitcpio() {
+ mkdir -p ${work_dir}/airootfs/etc/initcpio/hooks
+ mkdir -p ${work_dir}/airootfs/etc/initcpio/install
+ cp /usr/lib/initcpio/hooks/archiso ${work_dir}/airootfs/etc/initcpio/hooks
+ cp /usr/lib/initcpio/install/archiso ${work_dir}/airootfs/etc/initcpio/install
+
+ cat << EOF > "${work_dir}/airootfs/etc/mkinitcpio-archiso.conf"
+HOOKS=(base udev archiso block filesystems)
+EOF
+
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -r 'mkinitcpio -c /etc/mkinitcpio-archiso.conf -k /boot/vmlinuz-linux -g /boot/archiso.img' run
+}
+
+# Enable autologin on tty1
+make_autologin() {
+ mkdir -p "${work_dir}/airootfs/etc/systemd/system/getty@tty1.service.d"
+ cat << EOF > "${work_dir}/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf"
+[Service]
+ExecStart=
+ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux
+EOF
+}
+
+make_networking() {
+ local iface
+ iface="ens1"
+ cat << EOF > "${work_dir}/airootfs/etc/systemd/network/20-wired.network"
+[Match]
+Name=${iface}
+
+[Network]
+DHCP=ipv4
+EOF
+
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -r "systemctl enable systemd-networkd.service systemd-networkd-wait-online.service systemd-resolved.service" run
+}
+
+# Uncomment the servers in the mirrorlist and randomise their order
+make_pacman_mirrorlist() {
+ local mirrorlist
+ mirrorlist="${work_dir}/airootfs/etc/pacman.d/mirrorlist"
+ cat "${mirrorlist}" | grep "Server" | sed 's/^#Server/Server/g' | sort -R \
+ > "${mirrorlist}.new"
+ mv "${mirrorlist}.new" "${mirrorlist}"
+}
+
+# Initialize pacman-key
+make_pacman_key_init() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -r "pacman-key --init" run
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -r "pacman-key --populate archlinux" run
+}
+
+make_clone_installer_at_run_time() {
+ cat << EOF > "${work_dir}/airootfs/root/autorun.sh"
+#!/usr/bin/env bash
+set -uxo pipefail
+git clone -b "autoarch-blog" "http://gitolite.lan/autoarch.git" "\${HOME}/autoarch"
+bash "\${HOME}/autoarch/install/main.sh"
+EOF
+}
+
+make_clone_installer_at_build_time() {
+ git clone -b "autoarch-blog" "http://gitolite.lan/autoarch.git" "${work_dir}/airootfs/root/autoarch"
+ cat << EOF > "${work_dir}/airootfs/root/autorun.sh"
+#!/usr/bin/env bash
+set -uxo pipefail
+bash "\${HOME}/autoarch/install/main.sh"
+EOF
+}
+
+# Set up automatic install
+make_setup_automatic_install() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" \
+ -p arch-install-scripts -p git install
+
+ # Automatically run the installer on boot
+ cat << EOF > "${work_dir}/airootfs/root/.profile"
+#!/usr/bin/env bash
+set -uxo pipefail
+
+if [[ \$(tty) == "/dev/tty1" ]]; then
+ bash "\${HOME}/autorun.sh"
+fi
+EOF
+
+
+ # Option 1
+ make_clone_installer_at_run_time
+ # Option 2
+ #make_clone_installer_at_build_time
+
+ chmod +x "${work_dir}/airootfs/root/autorun.sh"
+}
+
+# Prepare ${install_dir}/boot/
+make_boot() {
+ mkdir -p "${work_dir}/iso/${install_dir}/boot/${arch}"
+ cp "${work_dir}/airootfs/boot/archiso.img" "${work_dir}/iso/${install_dir}/boot/${arch}/archiso.img"
+ cp "${work_dir}/airootfs/boot/vmlinuz-linux" "${work_dir}/iso/${install_dir}/boot/${arch}/vmlinuz"
+}
+
+# Prepare /${install_dir}/boot/syslinux
+make_syslinux() {
+ mkdir -p ${work_dir}/iso/${install_dir}/boot/syslinux
+ sed "s|%ARCHISO_LABEL%|${iso_label}|g;
+ s|%INSTALL_DIR%|${install_dir}|g;
+ s|%ARCH%|${arch}|g" "${script_path}/syslinux/syslinux.cfg" > "${work_dir}/iso/${install_dir}/boot/syslinux/syslinux.cfg"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/ldlinux.c32" "${work_dir}/iso/${install_dir}/boot/syslinux/"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/menu.c32" "${work_dir}/iso/${install_dir}/boot/syslinux/"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/libutil.c32" "${work_dir}/iso/${install_dir}/boot/syslinux/"
+}
+
+# Prepare /isolinux
+make_isolinux() {
+ mkdir -p ${work_dir}/iso/isolinux
+ sed "s|%INSTALL_DIR%|${install_dir}|g" "${script_path}/isolinux/isolinux.cfg" > "${work_dir}/iso/isolinux/isolinux.cfg"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/isolinux.bin" "${work_dir}/iso/isolinux/"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/isohdpfx.bin" "${work_dir}/iso/isolinux/"
+ cp "${work_dir}/airootfs/usr/lib/syslinux/bios/ldlinux.c32" "${work_dir}/iso/isolinux/"
+}
+
+# Build airootfs filesystem image
+make_prepare() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" prepare
+}
+
+# Build ISO
+make_iso() {
+ mkarchiso -v -w "${work_dir}" -D "${install_dir}" -L "${iso_label}" -o "${out_dir}" iso "${iso_name}-${iso_version}-${arch}.iso"
+}
+
+# Check root
+[[ "$(id -u)" == "0" ]] || (echo "Need root"; exit 1)
+
+# First nuke the working directories
+rm -rf "${out_dir}"
+rm -rf "${work_dir}"
+
+run_once make_basefs
+run_once make_install_kernel
+run_once make_setup_mkinitcpio
+run_once make_networking
+run_once make_autologin
+run_once make_pacman_mirrorlist
+run_once make_pacman_key_init
+run_once make_setup_automatic_install
+run_once make_boot
+run_once make_syslinux
+run_once make_isolinux
+run_once make_prepare
+run_once make_iso
diff --git a/build/syslinux/syslinux.cfg b/build/syslinux/syslinux.cfg
new file mode 100644
index 0000000..d87f78b
--- /dev/null
+++ b/build/syslinux/syslinux.cfg
@@ -0,0 +1,7 @@
+PROMPT 0
+DEFAULT arch
+
+LABEL arch
+ LINUX boot/%ARCH%/vmlinuz
+ INITRD boot/%ARCH%/archiso.img
+ APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL%
diff --git a/install/config.sh b/install/config.sh
new file mode 100644
index 0000000..39e17b6
--- /dev/null
+++ b/install/config.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# autoarch/install/config.sh
+# Configuration variables for the autoarch install script.
+set -euxo pipefail
+
+# The ethernet interface used for networking.
+export iface="ens1"
+
+# The timezone of the installed system.
+export timezone="Europe/London"
+
+# The hostname of the installed system.
+export hname="automatic"
+
+# The locale of the installed system.
+export userlocale="en_US.UTF-8"
+
+# The disk that will be formatted to install Arch.
+export disk="vda"
+
+# The username of the created account.
+export user="archuser"
+
+# The initial password of the created account.
+export initial_pass="changeme"
+
+# OPTIONAL: An SSH key that will be added to the created account.
+export ssh_key="ssh-ed25519 ABCDE example"
+
+# OPTIONAL: A custom SSH port to be used instead of the default 22.
+export ssh_port="10022"
+
+# OPTIONAL: A pacman mirror that will be used instead of the defaults.
+export pacman_mirror="http://pacman.lan/archlinux/\$repo/os/\$arch"
+
+# OPTIONAL: Create a swap file of this size on the installed system.
+export swap_size="2G"
+
+# OPTIONAL: A dotfiles repository that will be executed as the created account.
+# The repository should contain a `main.sh` file that will perform all of the
+# necessary symlinking.
+export dotfiles_repo="http://gitolite.lan/dotfiles.git"
diff --git a/install/main.sh b/install/main.sh
new file mode 100755
index 0000000..d9ceeac
--- /dev/null
+++ b/install/main.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# autoarch/install/main.sh
+# The autoarch automatic installation script.
+# See 'config.sh' for configuration parameters.
+set -euxo pipefail
+
+script_path=$(dirname "$(readlink -f "${0}")")
+# shellcheck source=config.sh
+source "${script_path}/config.sh"
+
+bash "${script_path}/stage1.sh"
+
+cp -a "${script_path}/"{config.sh,stage2.sh,packages.list} "/mnt/root/"
+arch-chroot "/mnt" bash "/root/stage2.sh"
+rm -f "/mnt/root/"{config.sh,stage2.sh,packages.list}
+
+cp -a "${script_path}/"{config.sh,stage3.sh} "/mnt/home/${user}/"
+systemd-nspawn -D /mnt \
+ sudo -u "${user}" bash -c "pushd \${HOME}; ./stage3.sh; popd"
+rm -f "/mnt/home/${user}/"{config.sh,stage3.sh}
+
+echo "Installation complete."
diff --git a/install/packages.list b/install/packages.list
new file mode 100644
index 0000000..a445dd5
--- /dev/null
+++ b/install/packages.list
@@ -0,0 +1,33 @@
+# autoarch/install/packages.list
+# Packages to install on the new system.
+# One per line. Lines beginning in # are ignored.
+
+base
+linux
+syslinux
+sudo
+openssh
+
+# custom packages below
+base-devel
+shellcheck
+coreutils
+entr
+git
+glibc
+htop
+ncdu
+python
+python-virtualenv
+ranger
+rsync
+the_silver_searcher
+tig
+tmux
+vim
+python
+go
+go-tools
+llvm
+clang
+rustup
diff --git a/install/stage1.sh b/install/stage1.sh
new file mode 100644
index 0000000..97f8263
--- /dev/null
+++ b/install/stage1.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# autoarch/install/stage1.sh
+# This script runs as the first stage in order to create the chroot.
+# See 'config.sh' for configuration parameters.
+set -euxo pipefail
+
+script_path=$(dirname "$(readlink -f "${0}")")
+# shellcheck source=config.sh
+source "${script_path}/config.sh"
+
+partition_and_mount_disk() {
+ echo "start=2048,type=83" | sfdisk "/dev/${disk}"
+
+ # Nuke the partition header
+ dd if=/dev/urandom of="/dev/${disk}1" bs=1M count=4
+ # Clear it to avoid potential misidentification
+ dd if=/dev/zero of="/dev/${disk}1" bs=1M count=4
+
+ mkfs.ext4 "/dev/${disk}1"
+ mount "/dev/${disk}"1 /mnt
+}
+
+create_and_enable_swap() {
+ fallocate -l "${swap_size}" /mnt/swapfile
+ chmod 0600 /mnt/swapfile
+ mkswap /mnt/swapfile
+ swapon /mnt/swapfile
+}
+
+# Check root
+[[ "$(id -u)" == "0" ]] || (echo "Need root"; exit 1)
+
+timedatectl set-ntp true
+partition_and_mount_disk
+if [[ -v swap_size ]]; then
+ create_and_enable_swap
+fi
+
+if [[ -v pacman_mirror ]]; then
+ echo "Server = ${pacman_mirror}" > "/etc/pacman.d/mirrorlist"
+fi
+
+# Install packages
+grep -v "^#" "${script_path}/packages.list" | xargs pacstrap /mnt
+
+echo "autoarch stage1 complete"
diff --git a/install/stage2.sh b/install/stage2.sh
new file mode 100755
index 0000000..0d7712f
--- /dev/null
+++ b/install/stage2.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+# autoarch/install/stage2.sh
+# This stage runs within the chroot as the root user.
+set -euxo pipefail
+
+script_path=$(dirname "$(readlink -f "${0}")")
+# shellcheck source=config.sh
+source "${script_path}/config.sh"
+
+setup_networking() {
+ cat << EOF > "/etc/systemd/network/20-wired.network"
+[Match]
+Name=${iface}
+
+[Network]
+DHCP=ipv4
+EOF
+
+ systemctl enable systemd-networkd.service
+ systemctl enable systemd-networkd-wait-online.service
+ systemctl enable systemd-resolved.service
+}
+
+set_timezone() {
+ ln -sf "/usr/share/zoneinfo/${timezone}" /etc/localtime
+ hwclock --systohc
+}
+
+set_hostname() {
+ echo "${hname}" > /etc/hostname
+}
+
+set_locale() {
+ echo "${userlocale} UTF-8" > /etc/locale.gen
+ locale-gen
+ echo "LANG=${userlocale}" > /etc/locale.conf
+}
+
+install_bootloader() {
+ syslinux-install_update -iam
+ cat << EOF > "/boot/syslinux/syslinux.cfg"
+PROMPT 0
+DEFAULT arch
+
+LABEL arch
+ LINUX ../vmlinuz-linux
+ INITRD ../initramfs-linux.img
+ APPEND root=/dev/${disk}1 rw init=/usr/lib/systemd/systemd
+EOF
+}
+
+create_user() {
+ useradd -m -G wheel -s /bin/bash "${user}"
+ echo "${user}:${initial_pass}" | chpasswd
+}
+
+install_sudo() {
+ cat << EOF > "/etc/sudoers"
+root ALL=(ALL) ALL
+%wheel ALL=(ALL) ALL
+EOF
+}
+
+install_ssh() {
+ if [[ -v ssh_port ]]; then
+ sed -i "s/^#Port 22/Port ${ssh_port}/g" "/etc/ssh/sshd_config"
+ fi
+
+ # Disable password authentication
+ sed -i "s/^#PasswordAuthentication yes/PasswordAuthentication no/g" \
+ "/etc/ssh/sshd_config"
+ sed -i "s/^UsePAM yes/UsePAM no/g" \
+ "/etc/ssh/sshd_config"
+
+ systemctl enable sshd.service
+
+ mkdir -p "/home/${user}/.ssh"
+
+ if [[ -v ssh_key ]]; then
+ echo "${ssh_key}" > "/home/${user}/.ssh/authorized_keys"
+ else
+ touch "/home/${user}/.ssh/authorized_keys"
+ fi
+
+ chown -R "${user}:${user}" "/home/${user}/.ssh"
+ chmod 0400 "/home/${user}/.ssh/authorized_keys"
+ chmod 0700 "/home/${user}/.ssh"
+}
+
+install_aur_packages() {
+ echo "TODO universal-ctags-git"
+}
+
+# Check root
+[[ "$(id -u)" == "0" ]] || (echo "Need root"; exit 1)
+
+setup_networking
+set_hostname
+set_timezone
+set_locale
+
+install_bootloader
+create_user
+install_sudo
+install_ssh
+
+install_aur_packages
+
+echo "autoarch stage2 complete"
diff --git a/install/stage3.sh b/install/stage3.sh
new file mode 100755
index 0000000..8dc9626
--- /dev/null
+++ b/install/stage3.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+# autoarch/install/stage3.sh
+# This stage runs within the chroot as the created user.
+set -euxo pipefail
+
+script_path=$(dirname "$(readlink -f "${0}")")
+# shellcheck source=config.sh
+source "${script_path}/config.sh"
+
+# Check username
+[[ "$(whoami)" == "${user}" ]]
+
+# Install dotfiles
+if [[ -v dotfiles_repo ]]; then
+ mkdir -p "${HOME}/git"
+ git clone "${dotfiles_repo}" "${HOME}/git/dotfiles"
+ bash "${HOME}/git/dotfiles/main.sh"
+fi
+
+echo "autoarch stage3 complete"