commit a906a3c9f6e7ddb21d49b9d39a572b0a0662bb16 Author: ryan Date: Wed Jan 31 01:51:17 2024 -0500 initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..53752db --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +output diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfe87f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +out +*.swp diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c4f6cc6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM archlinux + +RUN pacman -Syu --noconfirm archiso +ADD configs /configs +WORKDIR / + +ENTRYPOINT ["/usr/bin/bash", "-x", "/usr/bin/mkarchiso", "-v", "-w", "/work", "-o", "/out"] +CMD "/usr/share/archiso/configs/baseline" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..753a082 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# Known configs: +# * airgap + +.PHONY: default +default: out/archlinux-baseline-arch-airgap-x86_64.iso + +out/archlinux-baseline-arch-%-x86_64.iso: configs/% + $(MAKE) docker-airgap-builder + mkdir -p out + docker run --rm --privileged --volume "$(PWD)/out:/out" airgap-builder $< + +.PHONY: docker-airgap-builder +docker-airgap-builder: + docker build -t airgap-builder . diff --git a/README.md b/README.md new file mode 100644 index 0000000..f12099c --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Airgap NG + +A slim version of Arch Linux intended to run on airgapped (always-offline) +systems. The builder runs in Docker using privileged mode. + +## Building + +Requirements: + +* `docker` +* `make` + +The default target is the `airgap` image, but a different one can be specified. + +```sh +config="airgap" +make "out/archlinux-baseline-arch-${config}-x86_64.iso" +``` + +## Flashing + +Assuming your block device is `/dev/sdb`: + +```sh +pv < out/archlinux-baseline-arch-airgap-x86_64.iso | sudo dd of=/dev/sdb +``` + +Alternatively, if `pv` is not installed, the following can be run: + +```sh +sudo dd if=out/archlinux-baseline-arch-airgap-x86_64.iso of=/dev/sdb +``` diff --git a/configs/airgap/airootfs/etc/locale.conf b/configs/airgap/airootfs/etc/locale.conf new file mode 100644 index 0000000..f9c983c --- /dev/null +++ b/configs/airgap/airootfs/etc/locale.conf @@ -0,0 +1 @@ +LANG=C.UTF-8 diff --git a/configs/airgap/airootfs/etc/localtime b/configs/airgap/airootfs/etc/localtime new file mode 120000 index 0000000..0e35b57 --- /dev/null +++ b/configs/airgap/airootfs/etc/localtime @@ -0,0 +1 @@ +/usr/share/zoneinfo/UTC \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/mkinitcpio.conf.d/archiso.conf b/configs/airgap/airootfs/etc/mkinitcpio.conf.d/archiso.conf new file mode 100644 index 0000000..fd9cd48 --- /dev/null +++ b/configs/airgap/airootfs/etc/mkinitcpio.conf.d/archiso.conf @@ -0,0 +1 @@ +HOOKS=(base udev modconf archiso block filesystems) diff --git a/configs/airgap/airootfs/etc/mkinitcpio.d/linux.preset b/configs/airgap/airootfs/etc/mkinitcpio.d/linux.preset new file mode 100644 index 0000000..8e85205 --- /dev/null +++ b/configs/airgap/airootfs/etc/mkinitcpio.d/linux.preset @@ -0,0 +1,8 @@ +# mkinitcpio preset file for the 'linux' package on archiso + +PRESETS=('archiso') + +ALL_kver='/boot/vmlinuz-linux' +archiso_config='/etc/mkinitcpio.conf.d/archiso.conf' + +archiso_image="/boot/initramfs-linux.img" diff --git a/configs/airgap/airootfs/etc/shadow b/configs/airgap/airootfs/etc/shadow new file mode 100644 index 0000000..7edfd69 --- /dev/null +++ b/configs/airgap/airootfs/etc/shadow @@ -0,0 +1 @@ +root::14871:::::: diff --git a/configs/airgap/airootfs/etc/sysctl.d/10-silence-kernel.conf b/configs/airgap/airootfs/etc/sysctl.d/10-silence-kernel.conf new file mode 100644 index 0000000..05d49af --- /dev/null +++ b/configs/airgap/airootfs/etc/sysctl.d/10-silence-kernel.conf @@ -0,0 +1 @@ +kernel.printk = 0 0 0 0 diff --git a/configs/airgap/airootfs/etc/systemd/network.conf.d/ipv6-privacy-extensions.conf b/configs/airgap/airootfs/etc/systemd/network.conf.d/ipv6-privacy-extensions.conf new file mode 100644 index 0000000..0e9ceb4 --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/network.conf.d/ipv6-privacy-extensions.conf @@ -0,0 +1,2 @@ +[Network] +IPv6PrivacyExtensions=yes diff --git a/configs/airgap/airootfs/etc/systemd/network/20-ethernet.network b/configs/airgap/airootfs/etc/systemd/network/20-ethernet.network new file mode 100644 index 0000000..0e4287b --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/network/20-ethernet.network @@ -0,0 +1,13 @@ +[Match] +# Matching with "Type=ether" causes issues with containers because it also matches virtual Ethernet interfaces (veth*). +# See https://bugs.archlinux.org/task/70892 +# Instead match by globbing the network interface name. +Name=en* +Name=eth* + +[Link] +RequiredForOnline=routable + +[Network] +DHCP=yes +MulticastDNS=yes diff --git a/configs/airgap/airootfs/etc/systemd/resolved.conf.d/archiso.conf b/configs/airgap/airootfs/etc/systemd/resolved.conf.d/archiso.conf new file mode 100644 index 0000000..636f3bd --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/resolved.conf.d/archiso.conf @@ -0,0 +1,4 @@ +# Default systemd-resolved configuration for archiso + +[Resolve] +MulticastDNS=yes diff --git a/configs/airgap/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator b/configs/airgap/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator new file mode 120000 index 0000000..dc1dc0c --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator @@ -0,0 +1 @@ +/dev/null \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service b/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service new file mode 120000 index 0000000..4c158e6 --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.service \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service b/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service new file mode 120000 index 0000000..4f6ae34 --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-resolved.service \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service b/configs/airgap/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service new file mode 120000 index 0000000..7d6ad92 --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd-wait-online.service \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket b/configs/airgap/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket new file mode 120000 index 0000000..51942c8 --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.socket \ No newline at end of file diff --git a/configs/airgap/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf b/configs/airgap/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf new file mode 100644 index 0000000..c9f9bce --- /dev/null +++ b/configs/airgap/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf @@ -0,0 +1,6 @@ +# Allow systemd-networkd-wait-online to succeed with one interface, otherwise, if multiple network interfaces exist, +# network-online.target gets needlessly delayed. +# See https://wiki.archlinux.org/title/systemd-networkd#systemd-networkd-wait-online +[Service] +ExecStart= +ExecStart=/usr/lib/systemd/systemd-networkd-wait-online --any diff --git a/configs/airgap/bootstrap_packages.x86_64 b/configs/airgap/bootstrap_packages.x86_64 new file mode 100644 index 0000000..64966d0 --- /dev/null +++ b/configs/airgap/bootstrap_packages.x86_64 @@ -0,0 +1,2 @@ +arch-install-scripts +base diff --git a/configs/airgap/efiboot/loader/entries/01-archiso-x86_64-linux.conf b/configs/airgap/efiboot/loader/entries/01-archiso-x86_64-linux.conf new file mode 100644 index 0000000..13b690c --- /dev/null +++ b/configs/airgap/efiboot/loader/entries/01-archiso-x86_64-linux.conf @@ -0,0 +1,4 @@ +title Airgap Arch Linux (x86_64, UEFI) +linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux +initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img +options archisobasedir=%INSTALL_DIR% archisodevice=UUID=%ARCHISO_UUID% diff --git a/configs/airgap/efiboot/loader/loader.conf b/configs/airgap/efiboot/loader/loader.conf new file mode 100644 index 0000000..17110fa --- /dev/null +++ b/configs/airgap/efiboot/loader/loader.conf @@ -0,0 +1,2 @@ +timeout 3 +default 01-archiso-x86_64-linux.conf diff --git a/configs/airgap/grub/grub.cfg b/configs/airgap/grub/grub.cfg new file mode 100644 index 0000000..dcdd214 --- /dev/null +++ b/configs/airgap/grub/grub.cfg @@ -0,0 +1,105 @@ +# Load partition table and file system modules +insmod part_gpt +insmod part_msdos +insmod fat +insmod iso9660 +insmod ntfs +insmod ntfscomp +insmod exfat +insmod udf + +# Use graphics-mode output +if loadfont "${prefix}/fonts/unicode.pf2" ; then + insmod all_video + set gfxmode="auto" + terminal_input console + terminal_output console +fi + +# Enable serial console +insmod serial +insmod usbserial_common +insmod usbserial_ftdi +insmod usbserial_pl2303 +insmod usbserial_usbdebug +if serial --unit=0 --speed=115200; then + terminal_input --append serial + terminal_output --append serial +fi + +# Search for the ISO volume +if [ -z "${ARCHISO_UUID}" ]; then + if [ -z "${ARCHISO_HINT}" ]; then + regexp --set=1:ARCHISO_HINT '^\(([^)]+)\)' "${cmdpath}" + fi + search --no-floppy --set=root --file '%ARCHISO_SEARCH_FILENAME%' --hint "${ARCHISO_HINT}" + probe --set ARCHISO_UUID --fs-uuid "${root}" +fi + +# Get a human readable platform identifier +if [ "${grub_platform}" == 'efi' ]; then + archiso_platform='UEFI' + if [ "${grub_cpu}" == 'x86_64' ]; then + archiso_platform="x64 ${archiso_platform}" + elif [ "${grub_cpu}" == 'i386' ]; then + archiso_platform="IA32 ${archiso_platform}" + else + archiso_platform="${grub_cpu} ${archiso_platform}" + fi +elif [ "${grub_platform}" == 'pc' ]; then + archiso_platform='BIOS' +else + archiso_platform="${grub_cpu} ${grub_platform}" +fi + +# Set default menu entry +default=archlinux +timeout=15 +timeout_style=menu + + +# Menu entries + +menuentry "Airgap Arch Linux (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisodevice=UUID=${ARCHISO_UUID} + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + +if [ "${grub_platform}" == 'efi' -a "${grub_cpu}" == 'x86_64' -a -f '/boot/memtest86+/memtest.efi' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest.efi + } +fi +if [ "${grub_platform}" == 'pc' -a -f '/boot/memtest86+/memtest' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest + } +fi +if [ "${grub_platform}" == 'efi' ]; then + if [ "${grub_cpu}" == 'x86_64' -a -f '/shellx64.efi' ]; then + menuentry 'UEFI Shell' { + chainloader /shellx64.efi + } + elif [ "${grub_cpu}" == "i386" -a -f '/shellia32.efi' ]; then + menuentry 'UEFI Shell' { + chainloader /shellia32.efi + } + fi + + menuentry 'UEFI Firmware Settings' --id 'uefi-firmware' { + fwsetup + } +fi + +menuentry 'System shutdown' --class shutdown --class poweroff { + echo 'System shutting down...' + halt +} + +menuentry 'System restart' --class reboot --class restart { + echo 'System rebooting...' + reboot +} diff --git a/configs/airgap/grub/loopback.cfg b/configs/airgap/grub/loopback.cfg new file mode 100644 index 0000000..466e951 --- /dev/null +++ b/configs/airgap/grub/loopback.cfg @@ -0,0 +1,73 @@ +# https://www.supergrubdisk.org/wiki/Loopback.cfg + +# Search for the ISO volume +search --no-floppy --set=archiso_img_dev --file "${iso_path}" +probe --set archiso_img_dev_uuid --fs-uuid "${archiso_img_dev}" + +# Get a human readable platform identifier +if [ "${grub_platform}" == 'efi' ]; then + archiso_platform='UEFI' + if [ "${grub_cpu}" == 'x86_64' ]; then + archiso_platform="x64 ${archiso_platform}" + elif [ "${grub_cpu}" == 'i386' ]; then + archiso_platform="IA32 ${archiso_platform}" + else + archiso_platform="${grub_cpu} ${archiso_platform}" + fi +elif [ "${grub_platform}" == 'pc' ]; then + archiso_platform='BIOS' +else + archiso_platform="${grub_cpu} ${grub_platform}" +fi + +# Set default menu entry +default=archlinux +timeout=15 +timeout_style=menu + + +# Menu entries + +menuentry "Airgap Arch Linux (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + +if [ "${grub_platform}" == 'efi' -a "${grub_cpu}" == 'x86_64' -a -f '/boot/memtest86+/memtest.efi' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest.efi + } +fi +if [ "${grub_platform}" == 'pc' -a -f '/boot/memtest86+/memtest' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest + } +fi +if [ "${grub_platform}" == 'efi' ]; then + if [ "${grub_cpu}" == 'x86_64' -a -f '/shellx64.efi' ]; then + menuentry 'UEFI Shell' { + chainloader /shellx64.efi + } + elif [ "${grub_cpu}" == "i386" -a -f '/shellia32.efi' ]; then + menuentry 'UEFI Shell' { + chainloader /shellia32.efi + } + fi + + menuentry 'UEFI Firmware Settings' --id 'uefi-firmware' { + fwsetup + } +fi + +menuentry 'System shutdown' --class shutdown --class poweroff { + echo 'System shutting down...' + halt +} + +menuentry 'System restart' --class reboot --class restart { + echo 'System rebooting...' + reboot +} diff --git a/configs/airgap/packages.x86_64 b/configs/airgap/packages.x86_64 new file mode 100644 index 0000000..ba46087 --- /dev/null +++ b/configs/airgap/packages.x86_64 @@ -0,0 +1,5 @@ +base +linux +mkinitcpio +mkinitcpio-archiso +syslinux diff --git a/configs/airgap/pacman.conf b/configs/airgap/pacman.conf new file mode 100644 index 0000000..f382fab --- /dev/null +++ b/configs/airgap/pacman.conf @@ -0,0 +1,98 @@ +# +# /etc/pacman.conf +# +# See the pacman.conf(5) manpage for option and repository directives + +# +# GENERAL OPTIONS +# +[options] +# The following paths are commented out with their default values listed. +# If you wish to use different paths, uncomment and update the paths. +#RootDir = / +#DBPath = /var/lib/pacman/ +#CacheDir = /var/cache/pacman/pkg/ +#LogFile = /var/log/pacman.log +#GPGDir = /etc/pacman.d/gnupg/ +#HookDir = /etc/pacman.d/hooks/ +HoldPkg = pacman glibc +#XferCommand = /usr/bin/curl -L -C - -f -o %o %u +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u +#CleanMethod = KeepInstalled +Architecture = auto + +# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup +#IgnorePkg = +#IgnoreGroup = + +#NoUpgrade = +#NoExtract = + +# Misc options +#UseSyslog +#Color +#NoProgressBar +# We cannot check disk space from within a chroot environment +#CheckSpace +#VerbosePkgLists +ParallelDownloads = 5 + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional +#RemoteFileSigLevel = Required + +# NOTE: You must run `pacman-key --init` before first using pacman; the local +# keyring can then be populated with the keys of all official Arch Linux +# packagers with `pacman-key --populate archlinux`. + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here +# - local/custom mirrors can be added here or in separate files +# - repositories listed first will take precedence when packages +# have identical names, regardless of version number +# - URLs will have $repo replaced by the name of the current repo +# - URLs will have $arch replaced by the name of the architecture +# +# Repository entries are of the format: +# [repo-name] +# Server = ServerName +# Include = IncludePath +# +# The header [repo-name] is crucial - it must be present and +# uncommented to enable the repo. +# + +# The testing repositories are disabled by default. To enable, uncomment the +# repo name header and Include lines. You can add preferred servers immediately +# after the header, and they will be used before the default mirrors. + +#[core-testing] +#Include = /etc/pacman.d/mirrorlist + +[core] +Include = /etc/pacman.d/mirrorlist + +#[extra-testing] +#Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +# If you want to run 32 bit applications on your x86_64 system, +# enable the multilib repositories as required here. + +#[multilib-testing] +#Include = /etc/pacman.d/mirrorlist + +#[multilib] +#Include = /etc/pacman.d/mirrorlist + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#SigLevel = Optional TrustAll +#Server = file:///home/custompkgs diff --git a/configs/airgap/profiledef.sh b/configs/airgap/profiledef.sh new file mode 100644 index 0000000..cd0f387 --- /dev/null +++ b/configs/airgap/profiledef.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 + +iso_name="archlinux-baseline" +iso_label="AIRGAP" +iso_publisher="Distrust." +iso_application="Airgap Linux" +iso_version="arch-airgap" +install_dir="arch" +buildmodes=('iso') +bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito' + 'uefi-ia32.grub.esp' 'uefi-x64.grub.esp' + 'uefi-ia32.grub.eltorito' 'uefi-x64.grub.eltorito') +arch="x86_64" +pacman_conf="pacman.conf" +airootfs_image_type="squashfs" +# airootfs_image_tool_options=('-zlzma,109' -E 'ztailpacking,fragments,dedupe') +# airootfs_image_tool_options=('-zlzma,109' -E 'ztailpacking,fragments,dedupe') +file_permissions=( + ["/etc/shadow"]="0:0:400" +) diff --git a/configs/airgap/syslinux/syslinux-linux.cfg b/configs/airgap/syslinux/syslinux-linux.cfg new file mode 100644 index 0000000..9e6c4df --- /dev/null +++ b/configs/airgap/syslinux/syslinux-linux.cfg @@ -0,0 +1,5 @@ +LABEL arch +MENU LABEL Airgap Arch Linux (x86_64, BIOS) +LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archisodevice=UUID=%ARCHISO_UUID% diff --git a/configs/airgap/syslinux/syslinux.cfg b/configs/airgap/syslinux/syslinux.cfg new file mode 100644 index 0000000..5e87c04 --- /dev/null +++ b/configs/airgap/syslinux/syslinux.cfg @@ -0,0 +1,9 @@ +SERIAL 0 115200 +UI menu.c32 +MENU TITLE Airgap Arch Linux +MENU CLEAR + +DEFAULT arch +TIMEOUT 30 + +INCLUDE syslinux-linux.cfg