Initial commit

This commit is contained in:
Antoine Martin 2022-03-04 14:33:13 -05:00
commit cd96dcb95a
13 changed files with 726 additions and 0 deletions

196
Makefile.alpinelinux Normal file
View file

@ -0,0 +1,196 @@
# Makefile for Archlinux packages build
#
# For "API" documentation check Makefile.generic
#
# Variables supposed to be in component's Makefile.builder:
# ALPINE_BUILD_DIRS - list of alpinelinux directories containing build sripts (PKGFILES...)
### Variables required as per Makefile.generic:
#
# PACKAGE_LIST - list of packages to build. Targets 'build-dep', 'package' and 'copy-out'
# will be run for each word on the list, with PACKAGE set to current word
# DIST_BUILD_DIR - basedir for sources inside of chroot - relative to
# CHROOT_DIR (qubes-src will be created in this directory)
#
PACKAGE_LIST = $(ALPINE_BUILD_DIRS)
DIST_BUILD_DIR = /home/user
### Local variables
RUN_AS_USER = user
ALPINELINUX_MIRROR ?= https://dl-cdn.alpinelinux.org/alpine
DEBUG ?= 0
ifneq ($(DEBUG),0)
$(info ╔══ DEBUG ══════════════════════════════════════════════════════════════════════)
$(info ║ Repo Variables)
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ SRC_DIR: $(SRC_DIR)) # qubes-src
$(info ║ CHROOT_DIR: $(CHROOT_DIR)) # /home/user/qubes-builder/chroot-alpinelinux
$(info ║ BUILDER_REPO_DIR: $(BUILDER_REPO_DIR)) # /home/user/qubes-builder/qubes-packages-mirror-repo/alpinelinux
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ Chroot Variables)
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ CHROOT_DIR: $(CHROOT_DIR)) #
$(info ║ DIST_BUILD_DIR: $(DIST_BUILD_DIR)) # /home/user
$(info ║ DIST_SRC: $(DIST_SRC)) # /home/user/qubes-src/repo
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ Build Variables)
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ ALPINELINUX_PLUGIN_DIR: $(ALPINELINUX_PLUGIN_DIR)) # /home/user/qubes-builder/qubes-src/builder-alpinelinux
$(info ║ CACHEDIR: $(CACHEDIR)) # cache/alpinelinux
$(info ║ PACKAGE_LIST: $(PACKAGE_LIST)) # alpinelinux
$(info ║ DISTRIBUTION: $(DISTRIBUTION)) # alpinelinux
$(info ║ DIST: $(DIST)) #
$(info ║ COMPONENT: $(COMPONENT)) #
$(info ║ PACKAGE_SET: $(PACKAGE_SET)) # vm
$(info ║ CHROOT_ENV: $(CHROOT_ENV)) # BACKEND_VMM=xen
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ Repository Variables)
$(info ╠───────────────────────────────────────────────────────────────────────────────)
$(info ║ UPDATE_REPO: $(UPDATE_REPO)) #
$(info ║ TARGET_REPO: $(TARGET_REPO)) #
$(info ║ SNAPSHOT_REPO: $(SNAPSHOT_REPO)) #
$(info ║ SNAPSHOT_FILE: $(SNAPSHOT_FILE)) #
$(info ║ REPO_PROXY: $(REPO_PROXY)) #
$(info ║ ALPINELINUX_SRC_PREFIX: $(ALPINELINUX_SRC_PREFIX)) # http://mirrors.kernel.org/alpinelinux
$(info ║ ALPINELINUX_REL_VERSION: $(ALPINELINUX_REL_VERSION)) #
$(info ║ ALPINELINUX_MIRROR: $(ALPINELINUX_MIRROR)) # mirror.rackspace.com
$(info ╚═══════════════════════════════════════════════════════════════════════════════)
endif
define bin_packages
$(shell cd $(ORIG_SRC) && \
if [ 0`stat -c %Y $(OUTPUT_DIR)/$(notdir $(1)).list 2>/dev/null` -ge \
0`git log -1 --pretty=format:%ct` ]; then \
cat $(OUTPUT_DIR)/$(notdir $(1)).list; \
else \
echo unknown.package; \
fi)
endef
### Targets required by Makefile.generic to build packages:
# dist-prepare-chroot - initial preparation of chroot environment
# Specifically, load mounts for the build chroot
dist-prepare-chroot: $(CHROOT_DIR)/home/user/.prepared_base
@echo "--> Alpine linux dist-prepare-chroot (makefile):"
@mkdir -p "$(BUILDER_REPO_DIR)/pkgs"
@mkdir -p "$(CHROOT_DIR)/var/cache/apk"
@mkdir -p "$(CHROOT_DIR)/tmp/qubes-packages-mirror-repo"
# Create the build chroot, if it does not already exist
$(CHROOT_DIR)/home/user/.prepared_base: $(ALPINELINUX_PLUGIN_DIR)/prepare-chroot-builder
@echo "--> Alpine linux preparing build chroot environment"
@sudo -E "$(ALPINELINUX_PLUGIN_DIR)/prepare-chroot-builder" "$(CHROOT_DIR)" $(DIST) || exit 1
@touch "$(CHROOT_DIR)/home/user/.prepared_base"
# dist-prep - some preparation of sources (if needed)
dist-prep:
@true
# dist-build-dep - install build dependencies (should operate on chroot directory)
dist-build-dep:
@echo "--> Alpine linux dist-build-dep (makefile)"
@echo " --> Generate locales..."
@echo "en_US.UTF-8 UTF-8" | sudo tee -a $(CHROOT_DIR)/etc/locale.gen
@sudo $(CHROOT_ENV) chroot "$(CHROOT_DIR)" locale-gen
@echo "LANG=en_US.UTF-8" | sudo tee -a $(CHROOT_DIR)/etc/locale.conf
@sudo -E "$(ALPINELINUX_PLUGIN_DIR)/update-local-repo.sh" $(DIST)
# dist-package - compile package (should operate on chroot directory)
# TODO: makepkg doesn't seem to honor $http_proxy
dist-package:
@echo "--> Alpine linux dist-package (makefile)"
ifndef PACKAGE
$(error "PACKAGE need to be set!")
endif
@echo " --> Building package in $(DIST_SRC)"
sudo $(CHROOT_ENV) /usr/sbin/chroot "$(CHROOT_DIR)" sh -c -l "abuild -r $(ABUILD_ARGS)"
# dist-copy-out - copy compiled package out of chroot env; this target should
# move packages to ORIG_SRC (distro-specific subdir) and hardlink them to
# BUILDER_REPO_DIR
dist-copy-out: pkg_list_path = $(ORIG_SRC)/$(OUTPUT_DIR)/$(notdir $(PACKAGE)).list
dist-copy-out:
@echo "--> Archlinux dist-copy-out (makefile)"
@echo "--> Done:" >&3
@set -e;\
shopt -s nullglob;\
mkdir -p $(ORIG_SRC)/$(OUTPUT_DIR);\
echo -n > $(pkg_list_path);\
for arch_chroot_dir in $(CHROOT_DIR)/$(DIST_SRC)/; do\
arch_pkg_dir=$(ORIG_SRC)/$(OUTPUT_DIR);\
mkdir -p $$arch_pkg_dir;\
for pkg in $$arch_chroot_dir/*.pkg.tar.*; do\
echo " $$arch_pkg_dir/`basename $$pkg`" >&3 ;\
echo "$(OUTPUT_DIR)/`basename $$pkg`" >> $(pkg_list_path);\
done;\
mkdir -p $(BUILDER_REPO_DIR)/pkgs;\
ln -f -t $(BUILDER_REPO_DIR)/pkgs $$arch_chroot_dir/*.pkg.tar.*;\
done;\
mv -t $$arch_pkg_dir $$arch_chroot_dir/*.pkg.tar.*
### Additional targets
# Sign packages
sign: sign_client = $(if $(GNUPG),$(GNUPG),gpg)
sign:
@if [ -d $(ORIG_SRC)/$(OUTPUT_DIR) ]; then \
cd $(ORIG_SRC)/$(OUTPUT_DIR); \
for filename in *.pkg.tar.zst; do\
echo $$filename; \
$(sign_client) --yes --local-user "$(ALPINELINUX_SIGN_KEY)" --detach-sign -o "$$filename.sig" "$$filename";\
ln -f -t $(BUILDER_REPO_DIR)/pkgs "$$filename.sig";\
done; \
fi
# Copies requested packages (based on PACKAGE_SET, COMPONENT, DIST) to
# requested repository (UPDATE_REPO)
update-repo:
ifndef UPDATE_REPO
$(error "You need to specify destination repo in UPDATE_REPO variable")
endif
ifeq (,$(PACKAGE_LIST))
@true
else
ifdef SNAPSHOT_FILE
@echo -n > $(SNAPSHOT_FILE)
endif
mkdir -p $(UPDATE_REPO)/pkgs; \
for package in $(PACKAGE_LIST); do\
pkgnames=`cat $(ORIG_SRC)/$(OUTPUT_DIR)/$$package.list`;\
for pkgname in $$pkgnames; do\
ln -f $(ORIG_SRC)/$$pkgname $(UPDATE_REPO)/pkgs/ || exit 1;\
ln -f $(ORIG_SRC)/$$pkgname.sig $(UPDATE_REPO)/pkgs/ 2>/dev/null;\
if [ -n "$(SNAPSHOT_FILE)" ]; then \
echo $$pkgname >> "$(SNAPSHOT_FILE)"; \
fi; \
done; \
done
endif
update-repo-from-snapshot: packages = $(shell cat $(SNAPSHOT_FILE) 2>/dev/null)
update-repo-from-snapshot:
ifndef UPDATE_REPO
$(error "You need to specify destination repo in UPDATE_REPO variable")
endif
mkdir -p $(UPDATE_REPO)/pkgs; \
for f in $(packages); do \
ln -f $(subst /$(TARGET_REPO)/,/$(SNAPSHOT_REPO)/,$(UPDATE_REPO)/)pkgs/`basename $$f` $(UPDATE_REPO)/pkgs/ || exit 1; \
ln -f $(subst /$(TARGET_REPO)/,/$(SNAPSHOT_REPO)/,$(UPDATE_REPO)/)pkgs/`basename $$f`.sig $(UPDATE_REPO)/pkgs/ 2>/dev/null; \
done
check-repo: packages = $(foreach pkg,$(PACKAGE_LIST),$(call bin_packages,$(pkg)))
check-repo:
ifndef UPDATE_REPO
$(error "You need to specify destination repo in UPDATE_REPO variable")
endif
@if [ -n "$(strip $(packages))" ]; then \
cd $(ORIG_SRC) && ls $(addprefix $(UPDATE_REPO)/pkgs/, $(notdir $(packages))) >/dev/null 2>&1 || exit 1; \
else \
echo "`tput bold`No packages defined by $(PACKAGE_LIST), syntax error?`tput sgr0`"; \
exit 1; \
fi

8
Makefile.builder Normal file
View file

@ -0,0 +1,8 @@
ifeq ($(DIST),alpinelinux)
ALPINELINUX_PLUGIN_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
DISTRIBUTION := alpinelinux
BUILDER_MAKEFILE = $(ALPINELINUX_PLUGIN_DIR)Makefile.alpinelinux
TEMPLATE_SCRIPTS = $(ALPINELINUX_PLUGIN_DIR)scripts
endif
# vim: ft=make

36
prepare-chroot-base Executable file
View file

@ -0,0 +1,36 @@
#!/bin/sh
# vim: set ts=4 sw=4 sts=4 et :
### prepare-chroot-base : Create a (any) chroot instance of Archlinux
### May be called from ./scripts/01_install_core.sh or ./prepare-chroot-archlinux
echo "--> Alpine linux prepare-chroot-base"
INSTALLDIR="$1"
DISTRO="$2" # aka elsewhere as $DIST
BOOTSTRAP_DIR="${CACHEDIR}/bootstrap"
ALPINELINUX_PLUGIN_DIR="${ALPINELINUX_PLUGIN_DIR:-"${SCRIPTSDIR}/.."}"
ALPINELINUX_VERSION=${ALPINELINUX_VERSION:-latest-stable}
ALPINELINUX_MIRROR=${ALPINELINUX_MIRROR:-http://dl-cdn.alpinelinux.org/alpine}
set -e
if [ "$VERBOSE" -ge 2 ] || [ "$DEBUG" -gt 0 ]; then
set -x
fi
exit_prepare() {
local exit_code=$?
echo " --> Unbinding INSTALLDIR..."
umount ${BOOTSTRAP_DIR}/mnt || true
exit $exit_code
}
trap 'exit_prepare' 0 1 2 3 6 15
echo " --> Binding INSTALLDIR '${INSTALLDIR}' to bootstrap environment..."
mkdir -p "${BOOTSTRAP_DIR}/mnt"
mount --bind "$INSTALLDIR" "${BOOTSTRAP_DIR}/mnt"
echo " --> Installing core apk packages..."
"$BOOTSTRAP_DIR"/sbin/apk.static -X $ALPINELINUX_MIRROR/$ALPINELINUX_VERSION/main -U --allow-untrusted -p "$INSTALLDIR" --initdb add alpine-base
touch "${INSTALLDIR}/.prepared_base"

81
prepare-chroot-builder Executable file
View file

@ -0,0 +1,81 @@
#!/bin/sh
# vim: set ts=4 sw=4 sts=4 et :
### prepare-chroot-builder : Create the build chroot instance of Archlinux
### (in which to build Qubes packages)
echo "--> Alpine Linux prepare-chroot-builder"
PLUGIN_DIR="$(dirname $0)"
INSTALLDIR="$1"
DISTRO="$2"
SCRIPTSDIR=${ALPINELINUX_PLUGIN_DIR}scripts
export INSTALLDIR SCRIPTSDIR
# do not make volatile private key dir for the package builds themselves
# this is interpreted by scripts/alpine-chroot
SKIP_VOLATILE_SECRET_KEY_DIR=true
export SKIP_VOLATILE_SECRET_KEY_DIR
set -e
[ "$VERBOSE" -ge 1 -o "$DEBUG" -gt 0 ] && echo " --> INSTALLDIR: '$INSTALLDIR'"
[ "$VERBOSE" -ge 2 -o "$DEBUG" -gt 0 ] && set -x
# /home/user will exist if we've completed the build previously
if ! [ -d "${INSTALLDIR}/home/user" ]; then
# It's non-existance means this is likely the initial run, so build it
mkdir -p "$INSTALLDIR"
echo " --> Installing alpine linux build root:"
"${PLUGIN_DIR}/prepare-chroot-base" "$INSTALLDIR" "$DISTRO"
echo " --> Configure system accounts..."
[ -n "$SUDO_UID" ] && USER_OPTS="-u ${SUDO_UID}"
[ -n "$USER_UID" ] && USER_OPTS="-u ${USER_UID}"
if [ -n "$USER_GID" ]; then
chroot "$INSTALLDIR" /bin/addgroup -g "$USER_GID" user
elif [ -n "$SUDO_GID" ]; then
chroot "$INSTALLDIR" /bin/addgroup -g "$SUDO_GID" user
else
chroot "$INSTALLDIR" /bin/addgroup user
fi
chroot "$INSTALLDIR" /bin/sh -c \
"useradd -g user -G wheel $USER_OPTS -m user; su -c 'mkdir qubes-src' - user"
echo " --> Synchronize resolv.conf..."
cp /etc/resolv.conf "${INSTALLDIR}/etc/resolv.conf"
# Checking for free disk free space doesn't work in chroots
# echo " --> Comment out CheckSpace in pacman.conf..."
# sed 's/^ *CheckSpace/#CheckSpace/g' -i "${INSTALLDIR}/etc/pacman.conf"
echo " --> Installing required makepkg dependencies..."
pkgs="alpine-sdk"
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"http_proxy='${REPO_PROXY}' apk add $pkgs"
# makepkg internally calls sudo without '-E', so we need to add an
# env_keep to honor proxy settings
echo " --> Configure sudo..."
cat > "${INSTALLDIR}/etc/sudoers.d/qubes-build-user" <<EOF
Defaults env_keep += "http_proxy https_proxy ftp_proxy"
%wheel ALL=(ALL) NOPASSWD: ALL
EOF
# Register custom repository (it will be created later)
echo "file:///tmp/qubes-packages-mirror-repo/pkgs" | tee -a "${INSTALLDIR}/etc/apk/repositories"
# if [ -n "$USE_QUBES_REPO_VERSION" ]; then
# cat "${ALPINELINUX_PLUGIN_DIR}/repos/archlinux-qubes-repo-${USE_QUBES_REPO_VERSION}-current.conf" >> "${INSTALLDIR}/etc/pacman.conf"
# if [ "0$USE_QUBES_REPO_TESTING" -gt 0 ]; then
# cat "${ALPINELINUX_PLUGIN_DIR}repos/archlinux-qubes-repo-${USE_QUBES_REPO_VERSION}-current-testing.conf" \
# >> "${INSTALLDIR}/etc/pacman.conf"
# fi
# "${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" pacman-key --add - < \
# "${ALPINELINUX_PLUGIN_DIR}keys/qubes-repo-archlinux-key.asc"
# key_fpr=$(gpg --with-colons --show-key "${ALPINELINUX_PLUGIN_DIR}keys/qubes-repo-archlinux-key.asc" |\
# grep ^fpr: | cut -d : -f 10)
#"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" pacman-key --lsign "$key_fpr"
# fi
fi

34
scripts/00_prepare.sh Executable file
View file

@ -0,0 +1,34 @@
#! /bin/bash --
set -euo pipefail
echo "--> Alpine Linux 00_prepare.sh"
if [[ -n "${REPO_PROXY+x}" ]]; then
export "https_proxy=$REPO_PROXY" "http_proxy=$REPO_PROXY"
fi
ALPINELINUX_PLUGIN_DIR="${ALPINELINUX_PLUGIN_DIR:-"${SCRIPTSDIR}/.."}"
ALPINELINUX_VERSION=${ALPINELINUX_VERSION:-latest-stable}
ALPINELINUX_MIRROR=${ALPINELINUX_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}
ALPINELINUX_ARCH=${ALPINELINUX_ARCH:-x86_64}
APKTOOLS_VERSION=${APKTOOLS_VERSION:-2.12.7-r3}
APKTOOLS_FILE="${APKTOOLS_FILE:-apk-tools-static-"$APKTOOLS_VERSION".apk}"
APKTOOLS_URL="$ALPINELINUX_MIRROR/$ALPINELINUX_VERSION/main/$ALPINELINUX_ARCH/$APKTOOLS_FILE"
[ "$VERBOSE" -ge 2 -o "$DEBUG" -gt 0 ] && set -x
mkdir -p "${CACHEDIR}/pacman_cache"
echo " --> Downloading Alpine Linux bootstrap (v${APKTOOLS_VERSION-})..."
wget -N -P "$CACHEDIR" "$APKTOOLS_URL"
if [ "${CACHEDIR}/${APKTOOLS_FILE}" -nt "${CACHEDIR}/bootstrap/.extracted" ]; then
echo " --> Extracting bootstrap tarball (nuking previous directory)..."
rm -rf "${CACHEDIR}/bootstrap/"
mkdir -p "${CACHEDIR}/bootstrap"
# By default will extract to a "root.x86_64" directory; strip that off
tar -xzC "${CACHEDIR}/bootstrap" -f "${CACHEDIR}/${APKTOOLS_FILE}"
touch "${CACHEDIR}/bootstrap/.extracted"
else
echo " --> NB: Bootstrap tarball not newer than bootstrap directory, will use existing!"
fi

17
scripts/01_install_core.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash -e
# vim: set ts=4 sw=4 sts=4 et :
### 01_install_core.sh : Create build chroot install of Archlinux using pacstrap
echo "--> Alpine Linux 01_install_core.sh"
ALPINELINUX_PLUGIN_DIR="${ALPINELINUX_PLUGIN_DIR:-"${SCRIPTSDIR}/.."}"
ALPINELINUX_VERSION=${ALPINELINUX_VERSION:-latest-stable}
ALPINELINUX_SRC_PREFIX="${ALPINELINUX_SRC_PREFIX:-https://dl-cdn.alpinelinux.org/alpine/"$ALPINELINUX_VERSION"/main}"
set -e
[ "$VERBOSE" -ge 2 -o "$DEBUG" -gt 0 ] && set -x
# make sure pacman master private key is _not_ stored in the TemplateVM - see
# scripts/alpine-chroot for details
unset SKIP_VOLATILE_SECRET_KEY_DIR
"${ALPINELINUX_PLUGIN_DIR}/prepare-chroot-base" "$INSTALLDIR" "$DIST"

112
scripts/04_install_qubes.sh Executable file
View file

@ -0,0 +1,112 @@
#!/bin/bash -e
# vim: set ts=4 sw=4 sts=4 et :
### 04_install_qubes.sh : Prepare chroot instance as a Qubes template
echo "--> Archlinux 04_install_qubes.sh"
PACMAN_CACHE_DIR="${CACHEDIR}/pacman_cache"
PACMAN_CUSTOM_REPO_DIR="${PWD}/pkgs-for-template/${DIST}"
export PACMAN_CACHE_DIR PACMAN_CUSTOM_REPO_DIR "ALL_PROXY=$REPO_PROXY"
set -e
if [ "$VERBOSE" -ge 2 ] || [ "$DEBUG" -gt 0 ]; then
set -x
fi
echo " --> Enabling x86 repos..."
su -c "echo '[multilib]' >> $INSTALLDIR/etc/pacman.conf"
su -c "echo 'SigLevel = PackageRequired' >> $INSTALLDIR/etc/pacman.conf"
su -c "echo 'Include = /etc/pacman.d/mirrorlist' >> $INSTALLDIR/etc/pacman.conf"
sudo sed -Ei 's,^#(Server *= *https://mirrors\.kernel\.org/),\1,' "$INSTALLDIR/etc/pacman.d/mirrorlist"
echo " --> Updating Qubes custom repository..."
# Repo Add need packages to be added in the right version number order as it only keeps the last entered package version
# shellcheck disable=SC2016
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
'cd /tmp/qubes-packages-mirror-repo; for pkg in `ls -v pkgs/*.pkg.tar.zst`; do repo-add pkgs/qubes.db.tar.gz "$pkg"; done;'
chown -R --reference="$PACMAN_CUSTOM_REPO_DIR" "$PACMAN_CUSTOM_REPO_DIR"
echo " --> Registering Qubes custom repository..."
# shellcheck disable=SC2016
su -c 'echo "[qubes] " >> $INSTALLDIR/etc/pacman.conf'
# shellcheck disable=SC2016
su -c 'echo "SigLevel = Never " >> $INSTALLDIR/etc/pacman.conf'
# shellcheck disable=SC2016
su -c 'echo "Server = file:///tmp/qubes-packages-mirror-repo/pkgs " >> $INSTALLDIR/etc/pacman.conf'
echo " --> Synchronize resolv.conf..."
cp /etc/resolv.conf "${INSTALLDIR}/etc/resolv.conf"
echo " --> Updating pacman sources..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"until http_proxy='${REPO_PROXY}' pacman -Syu; do sleep 1; done"
echo " --> Checking available qubes packages (for debugging only)..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"until http_proxy='${REPO_PROXY}' pacman -Ss qubes; do sleep 1; done"
if [ -n "$USE_QUBES_REPO_VERSION" ]; then
# we don't check specific value here, assume correct branch of
# meta-packages component
echo " --> Installing repository qubes package..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"http_proxy='${REPO_PROXY}' pacman -S --noconfirm qubes-vm-repo"
if [ "0$USE_QUBES_REPO_TESTING" -gt 0 ]; then
echo " --> Enabling current-testing repository..."
ln -s "90-qubes-${USE_QUBES_REPO_VERSION}-current-testing.conf.disabled" \
"$INSTALLDIR/etc/pacman.d/90-qubes-${USE_QUBES_REPO_VERSION}-current-testing.conf"
# abort if the file doesn't exist
if ! [ -f "$INSTALLDIR/etc/pacman.d/90-qubes-${USE_QUBES_REPO_VERSION}-current-testing.conf" ]; then
ls -l "$INSTALLDIR/etc/pacman.d/"
exit 1
fi
fi
echo " --> Updating pacman sources..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"until http_proxy='${REPO_PROXY}' pacman -Syu; do sleep 1; done"
fi
echo " --> Installing mandatory qubes packages..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"until http_proxy='${REPO_PROXY}' pacman -S --noconfirm qubes-vm-dependencies; do sleep 1; done"
echo " --> Installing recommended qubes apps"
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"until http_proxy='${REPO_PROXY}' pacman -S --noconfirm qubes-vm-recommended; do sleep 1; done"
echo " --> Updating template fstab file..."
cat >> "${INSTALLDIR}/etc/fstab" <<EOF
#
# /etc/fstab: static file system information
#
# Templates Directories
/dev/mapper/dmroot / ext4 defaults,discard,noatime 1 1
/dev/xvdb /rw auto noauto,defaults,discard 1 2
/dev/xvdc1 swap swap defaults 0 0
# Template Binds
/rw/home /home none noauto,bind,defaults 0 0
/rw/usrlocal /usr/local none noauto,bind,defaults 0 0
# Template Customizations
tmpfs /dev/shm tmpfs defaults,size=1G 0 0
# This MUST be a ramfs, not a tmpfs! The data here is incredibly sensitive
# (allows root access) and must not be leaked to disk.
tmpfs /etc/pacman.d/gnupg/private-keys-v1.d ramfs defaults,noexec,nosuid,nodev,mode=600 0 0
EOF
echo " --> Configuring system to our preferences..."
# Name network devices using simple names (ethX)
ln -s /dev/null "${INSTALLDIR}/etc/udev/rules.d/80-net-name-slot.rules"
# Enable some locales (incl. UTF-8)
sed 's/#en_US/en_US/g' -i "${INSTALLDIR}/etc/locale.gen"
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" locale-gen
echo 'LANG=en_US.UTF-8' > "${INSTALLDIR}/etc/locale.conf"
# Creating a random file in /lib/modules to ensure that the directory in never deleted when packages are removed
mkdir -p "${INSTALLDIR}/lib/modules"
touch "${INSTALLDIR}/lib/modules/QUBES_NODELETE"
# Remove qubes local repository definition
sed '/\[qubes]/,+2 d' -i "${INSTALLDIR}/etc/pacman.conf"

40
scripts/09_cleanup.sh Executable file
View file

@ -0,0 +1,40 @@
#!/bin/bash -e
# vim: set ts=4 sw=4 sts=4 et :
### 09_cleanup.sh : Clean up the new chroot prior to image finalisation
echo "--> Archlinux 09_cleanup.sh"
set -e
[ "$VERBOSE" -ge 2 -o "$DEBUG" -gt 0 ] && set -x
# Remove unused packages and their dependencies (make dependencies)
cleanuppkgs="$("${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c 'pacman -Qdt | grep -v kernel | cut -d " " -f 1')"
if [ -n "$cleanuppkgs" ] ; then
echo " --> Packages that will be cleaned up: $cleanuppkgs"
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c "pacman --noconfirm -Rsc $cleanuppkgs"
else
echo " --> NB: No packages to clean up"
fi
echo " --> Removing video plugins..."
videopkgs="$("${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c 'pacman -Qs -q xf86-video')"
echo $videopkgs | "${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c 'pacman --noconfirm -Rsc -'
echo " --> Removing other font packages..."
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"pacman --noconfirm -Rsc xorg-fonts-100dpi xorg-fonts-75dpi"
# TODO: Be more deliberate here; is the umount necessary?
# Moreover, given where this script is called, should we be bothering
# alpine-chroot?
echo " --> Cleaning up pacman state..."
umount "${INSTALLDIR}/var/cache/pacman" || true
unset PACMAN_CACHE_DIR
"${SCRIPTSDIR}/alpine-chroot" "$INSTALLDIR" /bin/sh -c \
"pacman --noconfirm -Scc"
echo " --> Cleaning /etc/resolv.conf"
rm -f "${INSTALLDIR}/etc/resolv.conf"
cat > "${INSTALLDIR}/etc/resolv.conf" << EOF
# This file intentionally left blank
EOF

144
scripts/alpine-chroot Executable file
View file

@ -0,0 +1,144 @@
#!/bin/bash
###
### A stripped-down version of the 'arch-chroot' script bundled with Archlinux
###
### This version drops unused code and makes a fey key modifications,
### annotated where they occur below.
###
shopt -s extglob
die() { error "$@"; exit 1; }
chroot_add_mount() {
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
}
setup_volatile_secret_key_dir() {
if [ "$SKIP_VOLATILE_SECRET_KEY_DIR" = "true" ]; then
return
fi
# This directory stores secret GPG keys, so its contents must be kept secret
# at all costs. Anyone with access to the files in it can compromise the
# built TemplateVM and all VMs based on it.
secret_key_dir="$1/etc/pacman.d/gnupg/private-keys-v1.d" &&
# private-keys-v1.d does not exist before we create the tmpfs
mkdir -p -m 0755 -- "${secret_key_dir%/*}" &&
mkdir -p -m 0000 -- "$secret_key_dir" &&
# Create README
[[ -f "$secret_key_dir/README" ]] || cat > "$secret_key_dir/README" <<'EOF' &&
# Why is this directory immutable?
In QubesOS, a TemplateVMs root volume is readable by all AppVMs based on it.
Therefore, it cannot be used to store secret data.
Pacman relies on the secrecy of its master key, which is normally stored in
`/etc/pacman.d/gnupg/private-keys-v1.d`. Anyone who has this key can sign
packages that Pacman will accept. Therefore, this key must not be stored on the
root volume. Furthermore, a user might (quite reasonably) assume that there is
no sensitive information on a TemplateVMs private volume unless they have added
it explicitly. So the master key cannot be stored there either.
The only remaining option is to use an ephemeral key that is only kept in
memory. That is what QubesOS does: during the build process, a ramfs is mounted
over /etc/pacman.d/gnupg/private-keys-v1.d, so that the secret key is kept in
memory. When the ramfs is unmounted, the key is destroyed along with it.
There is one remaining problem: relying on a mount point is not fail-safe. If
the ramfs fails to mount, or if the user later runs operations like
`pacman-key --init`, a new master key will be generated. It will later be
leaked to AppVMs based on this template.
To prevent this potentially disasterous failure, QubesOS marks the directory as
immutable. This ensures that nobody (not even root) can create any files in it.
When GPG tries to write its secret key to disk, it will fail, preventing any
leakage.
P.S.: Why a ramfs and not a tmpfs? Data on a ramfs can never be paged out to
disk, which ensures that this key is never leaked to swap partitions. GPG
internally locks its memory into RAM to prevent similar problems.
EOF
# Mark private-keys-v1.d immutable, so that files (such as secret keys)
# cannot accidentally be created in it.
chattr -R +i -- "$secret_key_dir" &&
# See the README above for why this is a ramfs
chroot_add_mount pacman-privkeys "$secret_key_dir" -t ramfs -o mode=000,nosuid,noexec,nodev || exit
}
chroot_setup() {
CHROOT_ACTIVE_MOUNTS=()
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
trap 'chroot_teardown' EXIT
# alpine-chroot drops the conditional bind mount on the chroot path, as
# it seemed to shadow mounts set up before arch-chroot was invoked
# Set the correct permissions for mount points
chmod -- 0755 "$1/dev" "$1/run" &&
chmod -- 0555 "$1/proc" "$1/sys" &&
chmod -- 1777 "$1/tmp" &&
setup_volatile_secret_key_dir &&
chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
# alpine-chroot will never have occasion to use efivars, so don't bother
# mounting efivarfs here
chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid ||
exit
if [[ -d "$APKTOOLS_CACHE_DIR" ]]; then
APKTOOLS_CACHE_MOUNT_DIR="${APKTOOLS_CACHE_MOUNT_DIR:-$1/var/cache/apk}"
mkdir -p "$APKTOOLS_CACHE_MOUNT_DIR"
# Cached qubes packages may be from old runs and throw checksum errors
chroot_add_mount "$APKTOOLS_CACHE_DIR" "$APKTOOLS_CACHE_MOUNT_DIR" --bind
fi
if [[ -d "$APKTOOLS_CUSTOM_REPO_DIR" ]]; then
mkdir -p "$1/tmp/qubes-packages-mirror-repo"
chroot_add_mount "$APKTOOLS_CUSTOM_REPO_DIR" "$1/tmp/qubes-packages-mirror-repo" --bind
fi
}
chroot_teardown() {
# alpine-chroot kills gpg-agent, started by pacman-key, which otherwise
# keeps the mounts busy and prevents unmounting
pkill gpg-agent
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
unset CHROOT_ACTIVE_MOUNTS
}
usage() {
cat <<EOF
usage: ${0##*/} chroot-dir [command]
-h Print this help message
If 'command' is unspecified, ${0##*/} will launch /bin/sh.
EOF
}
if [[ -z $1 || $1 = @(-h|--help) ]]; then
usage
exit $(( $# ? 0 : 1 ))
fi
(( EUID == 0 )) || die 'This script must be run with root privileges'
chrootdir=$1
shift
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"
chroot_setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
# alpine-chroot already has /etc/resolv.conf managed by the builder
# scripts, so no need to bind to the host system's resolv.conf here
SHELL=/bin/sh unshare --fork --pid chroot "$chrootdir" "$@"

1
scripts/network Normal file
View file

@ -0,0 +1 @@
NETWORKING=yes

46
scripts/packages.list Normal file
View file

@ -0,0 +1,46 @@
# X
xorg
xterm
# Basic utils
ethtool
net-tools
sudo
wget
diffutils
# User env
ldns
tmux
vim
# Fonts
terminus-font
ttf-bitstream-vera
ttf-dejavu
ttf-inconsolata
ttf-linux-libertine
# Particularly good Unicode coverage:
noto-fonts
noto-fonts-emoji
# Gnome
gnome-settings-daemon
gtk-engines
gvfs
lxappearance
# XFCE
leafpad
thunar
thunar-volman
xfce4-terminal
xfce4-settings
# Major "productivity" applications
evince
firefox
thunderbird
# Hardening-related
checksec

View file

@ -0,0 +1,10 @@
# X
xorg
xterm
# Basic utils
ethtool
net-tools
sudo
wget
diffutils

1
scripts/resolv.conf Normal file
View file

@ -0,0 +1 @@
# This file intentionally left blank