commit 2437e6e467d1e88b3de80214ceac4e3113461e27 Author: Dustin C. Hatch Date: Mon Mar 7 08:53:58 2022 -0600 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a485625 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/_build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..73d15c0 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +SHELL ?= /bin/sh + +.PHONY: rootfs +rootfs: + $(SHELL) mkrootfs.sh _build/rootfs + +.PHONY: initramfs +initramfs: _build/rootfs/boot/initramfs.img + +_build/rootfs/boot/initramfs.img: \ + _build/rootfs/bin/dracut \ + mkinitramfs.sh + $(SHELL) mkinitramfs.sh _build/rootfs + +.PHONY: publish +publish: + $(SHELL) publish.sh _build/rootfs diff --git a/README.md b/README.md new file mode 100644 index 0000000..c049250 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Basement Hud Network-Booted OS + +Customized Fedora-based operating system distributed as an immutable +root filesystem image. + +## Building + +Must run on a Fedora host or privileged container. + +Build the root filesystem image: + +```sh +make rootfs +``` + +This will create an `_build/rootfs` directory containing the root filesystem +hierarchy. The `_build/rootfs/boot` directory contains the necessary files to +boot the system: + +* `efi`: This directory contains the Raspberry Pi firmware files +* `initramfs.img`: Early userspace image +* `vmlinuz`: Kernel image +* `basementhud.squashfs`: Root filesystem image + +The Raspberry Pi firmware, initramfs image, and kernel image files must be +served by a TFTP, while the rootfs image is served by NBD. + +Build the initramfs image: + +```sh +make initramfs +``` diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 0000000..3c7d506 --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,18 @@ +FROM registry.fedoraproject.org/fedora:35 + +ARG UID +ARG GID + +RUN groupadd -g ${GID} jenkins \ + && useradd -u ${UID} -g ${GID} -m -d /var/lib/jenkins -l jenkins + +RUN dnf install -y \ + findutils \ + make \ + openssh-clients \ + rsync \ + squashfs-tools \ + tar \ + unzip \ + util-linux \ + && dnf clean all diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile new file mode 100644 index 0000000..4ee4e72 --- /dev/null +++ b/ci/Jenkinsfile @@ -0,0 +1,67 @@ +DOCKER_BUILD_ARGS = '''\ +--build-arg UID=$(id -u) \ +--build-arg GID=$(id -g) \ +''' + +pipeline { + agent { + label 'docker && aarch64' + } + + options { + buildDiscarder logRotator(numToKeepStr: '5') + disableConcurrentBuilds() + } + + triggers { + pollSCM '' + } + + parameters { + booleanParam \ + name: 'Clean', + description: 'Clean the workspace and perform a full rebuild' + } + + stages { + stage('Build') { + agent { + dockerfile { + reuseNode true + dir 'ci' + args '--privileged -u 0:0' + additionalBuildArgs DOCKER_BUILD_ARGS + } + } + steps { + script { + if (params.Clean) { + sh 'rm -rf _build' + } + } + sh 'make rootfs initramfs' + } + } + + stage('Publish') { + when { + not { + changeRequest() + } + } + agent { + dockerfile { + reuseNode true + dir 'ci' + args '-v /etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts' + additionalBuildArgs DOCKER_BUILD_ARGS + } + } + steps { + sshagent(['jenkins-pxe']) { + sh 'make publish' + } + } + } + } +} diff --git a/cmdline.txt b/cmdline.txt new file mode 100644 index 0000000..ae627f0 --- /dev/null +++ b/cmdline.txt @@ -0,0 +1 @@ +rd.retry=3 rd.timeout=5 panic=5 audit=0 root=nbd:pxe0.pyrocufflink.blue:basementhud diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..770bb3e --- /dev/null +++ b/config.txt @@ -0,0 +1,55 @@ +# Raspberry Pi 4 +[pi4] +kernel=vmlinuz +dtoverlay=upstream-pi4 + +# Default Fedora configs for all Raspberry Pi Revisions +[all] +# Put the RPi into 64 bit mode +arm_64bit=1 + +# To use an external initramfs file +initramfs initramfs.img + +# Enable UART +# Only enable UART if you're going to use it as it has speed implications +# Serial console is ttyS0 on RPi3 and ttyAMA0 on all other variants +# u-boot will auto detect serial and pass corrent options to kernel if enabled +# Speed details: https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=141195 + +# Terrible hack to work around U-Boot issues on most devices +#enable_uart=1 + +# Early boot delay in the hope monitors are initialised enough to provide EDID +bootcode_delay=1 + +# We need this to be 32Mb to support VCHI services and drivers which use them +# but this isn't used by mainline VC4 driver so reduce to lowest supported value +# You need to set this to at least 80 for using the camera +gpu_mem=32 + +# Use eXtended firmware by default +start_x=1 + +# Stop the RPi turning on HDMI monitors on reboot +hdmi_ignore_cec_init=1 + +# New option to allow the firmware to load upstream dtb +# Will allow things like camera, touchscreen etc to work OOTB +upstream_kernel=1 + +# HAT and DT overlays. Documentation at Raspberry Pi here: +# https://www.raspberrypi.org/documentation/configuration/device-tree.md +# Each dtoverlay line is an individual HAT/overlay, multiple lines allowed +# dtoverlay=rpi-sense + +# Other options you can adjust for all Raspberry Pi Revisions +# https://www.raspberrypi.org/documentation/configuration/config-txt/README.md +# All options documented at http://elinux.org/RPiconfig +# for more options see http://elinux.org/RPi_config.txt + +# To use this on Fedora you need to use firmware provided device tree, not kernel +# For this functionality follow the following guide: +# https://fedoraproject.org/w/index.php?title=Architectures/ARM/Raspberry_Pi/HATs + +dtoverlay=disable-bt diff --git a/mkinitramfs.sh b/mkinitramfs.sh new file mode 100644 index 0000000..5bef67f --- /dev/null +++ b/mkinitramfs.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -eu + +destdir="$(readlink -e "$1")" + +kver=$(rpm --root "${destdir}" -q --qf '%{VERSION}-%{RELEASE}.%{ARCH}' kernel) +printf 'Building initramfs image for kernel %s\n' "${kver}" + +cd "${destdir}" +mkdir -p boot dev proc sys tmp +mountpoint -q dev || mount -o bind /dev dev +mountpoint -q proc || mount -t proc proc proc +mountpoint -q sys || mount -t sysfs sysfs sys +mountpoint -q tmp || mount -t tmpfs tmpfs tmp + +echo 'Copying kernel image to /boot/ ...' >&2 +cp -a "${destdir}"/lib/modules/"${kver}"/vmlinuz \ + "${destdir}"/boot/ +echo 'Running depmod ...' >&2 +chroot "${destdir}" depmod -a "${kver}" +echo 'Building initramfs image ...' 2>&1 +chroot "${destdir}" dracut \ + --no-hostonly \ + --add 'nbd' \ + --add-drivers 'genet nbd squashfs' \ + --force \ + /boot/initramfs.img \ + "${kver}" +echo 'Fixing boot file pemissions ...' 2>&1 +find "${destdir}"/boot \ + -type d -exec chmod a+rx {} + \ + -or \ + -type f -exec chmod a+r {} + + +umount tmp sys proc dev diff --git a/mkrootfs.sh b/mkrootfs.sh new file mode 100644 index 0000000..38bdbc7 --- /dev/null +++ b/mkrootfs.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +set -e + +srcdir="$(dirname "$0")" +destdir="$(mkdir -p "$1" && readlink -e "$1")" +dest=basementhud.squashfs +releasever=35 + +mkdir -p "${destdir}" + +dnf --installroot "${destdir}" install -y \ + --releasever ${releasever} \ + fedora-release + +cp /etc/dnf/dnf.conf "${destdir}/etc/dnf/" +echo install_weak_deps=False >> "${destdir}"/etc/dnf/dnf.conf + +dnf --installroot "${destdir}" install -y \ + bcm2711-firmware \ + dracut-network \ + firefox \ + hostname \ + kmod \ + matchbox-window-manager \ + mesa-dri-drivers \ + nbd \ + openssh-server \ + rsyslog \ + systemd \ + systemd-resolved \ + xinit \ + xorg-x11-server-Xorg \ + xset \ + -- + +dnf --installroot "${destdir}" install -y \ + --setopt tsflags=noscripts \ + kernel \ + -- + +cp -p \ + "${srcdir}"/cmdline.txt \ + "${srcdir}"/config.txt \ + "${destdir}"/boot/efi + +for x in ed25519 rsa ecdsa; do + chroot "${destdir}" /usr/libexec/openssh/sshd-keygen $x +done + +tar --owner root:0 -C "${srcdir}/overlay" -c . \ + | tar -C "${destdir}" -x +chown -R 1000:1000 "${destdir}"/home/user +if ! grep -q ^user: "${destdir}"/etc/group; then + chroot "${destdir}" groupadd -g 1000 user +fi +if ! grep -q ^user: "${destdir}"/etc/passwd; then + chroot "${destdir}" useradd -u 1000 -g 1000 user +fi +if ! grep -qE '^video:.*:.+:.*,?user' "${destdir}"/etc/group; then + chroot "${destdir}" gpasswd -a user video +fi +chroot "${destdir}" systemctl enable \ + systemd-time-wait-sync \ + systemd-timesyncd \ + xinit@user + +umask 0022 +mksquashfs "${destdir}" "${destdir}/boot/${dest}" \ + -comp lz4 \ + -noappend \ + -ef "${srcdir}"/squashfs-exclude.txt \ + -no-exports \ + -wildcards diff --git a/overlay/etc/fstab b/overlay/etc/fstab new file mode 100644 index 0000000..d4e89d7 --- /dev/null +++ b/overlay/etc/fstab @@ -0,0 +1,4 @@ +tmpfs /var/lib/rsyslog tmpfs defaults 0 0 +tmpfs /var/log tmpfs defaults 0 0 +tmpfs /var/lib/systemd tmpfs defaults 0 0 +tmpfs /var/lib/NetworkManager tmpfs defaults 0 0 diff --git a/overlay/etc/pam.d/xinit b/overlay/etc/pam.d/xinit new file mode 100644 index 0000000..d26d226 --- /dev/null +++ b/overlay/etc/pam.d/xinit @@ -0,0 +1,17 @@ +#%PAM-1.0 +auth substack system-auth +auth include postlogin +account required pam_nologin.so +account include system-auth +password include system-auth +# pam_selinux.so close should be the first session rule +session required pam_selinux.so close +session required pam_loginuid.so +# pam_selinux.so open should only be followed by sessions to be executed in the user context +session required pam_selinux.so open +session required pam_namespace.so +session optional pam_keyinit.so force revoke +session include system-auth +session required pam_exec.so /usr/local/libexec/home-overlay.sh +session include postlogin +-session optional pam_ck_connector.so diff --git a/overlay/etc/rsyslog.conf b/overlay/etc/rsyslog.conf new file mode 100644 index 0000000..0cb72d1 --- /dev/null +++ b/overlay/etc/rsyslog.conf @@ -0,0 +1,4 @@ +global(workDirectory="/var/lib/rsyslog") +module(load="imjournal" + StateFile="imjournal.state") +*.* @172.30.0.28 diff --git a/overlay/etc/systemd/system/xinit@.service b/overlay/etc/systemd/system/xinit@.service new file mode 100644 index 0000000..0d9a5cb --- /dev/null +++ b/overlay/etc/systemd/system/xinit@.service @@ -0,0 +1,21 @@ +[Unit] +Description=Start X.org for user %I +Wants=network-online.target +After=network-online.target +Wants=time-sync.target +After=time-sync.target + +[Service] +Type=simple +User=%I +WorkingDirectory=~ +PAMName=xinit +TTYPath=/dev/tty7 +StandardInput=tty +StandardOutput=journal +StandardError=journal +ExecStart=/usr/bin/startx +Restart=always + +[Install] +WantedBy=graphical.target diff --git a/overlay/etc/systemd/user/firefox.service b/overlay/etc/systemd/user/firefox.service new file mode 100644 index 0000000..ee4ac29 --- /dev/null +++ b/overlay/etc/systemd/user/firefox.service @@ -0,0 +1,9 @@ +[Unit] +Description=Firefox web browser + +[Service] +Type=simple +ExecStart=/usr/bin/firefox +Restart=always +StandardInput=null +StandardOutput=journal diff --git a/overlay/etc/systemd/user/graphical.target b/overlay/etc/systemd/user/graphical.target new file mode 100644 index 0000000..3830c13 --- /dev/null +++ b/overlay/etc/systemd/user/graphical.target @@ -0,0 +1,4 @@ +[Unit] +Description=Graphical Interface +After=default.target +AllowIsolate=yes diff --git a/overlay/etc/systemd/user/graphical.target.wants/firefox.service b/overlay/etc/systemd/user/graphical.target.wants/firefox.service new file mode 120000 index 0000000..9b4b5e4 --- /dev/null +++ b/overlay/etc/systemd/user/graphical.target.wants/firefox.service @@ -0,0 +1 @@ +../firefox.service \ No newline at end of file diff --git a/overlay/home/user/.xinitrc b/overlay/home/user/.xinitrc new file mode 100644 index 0000000..bdb1786 --- /dev/null +++ b/overlay/home/user/.xinitrc @@ -0,0 +1,7 @@ +#!/bin/sh + +xset s off +xset -dpms +systemctl --user import-environment DISPLAY +systemctl --user start graphical.target +exec matchbox-window-manager -use_titlebar no diff --git a/overlay/root/.ssh/authorized_keys b/overlay/root/.ssh/authorized_keys new file mode 100644 index 0000000..1d97f45 --- /dev/null +++ b/overlay/root/.ssh/authorized_keys @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJsL5fSylmiJmBtW0DH/viAAmtU2E/2M17GPvysiyRs+ dustin@rosalina diff --git a/overlay/usr/lib64/firefox/defaults/pref/autoconfig.js b/overlay/usr/lib64/firefox/defaults/pref/autoconfig.js new file mode 100644 index 0000000..7c6fba9 --- /dev/null +++ b/overlay/usr/lib64/firefox/defaults/pref/autoconfig.js @@ -0,0 +1,2 @@ +pref("general.config.filename", "firefox.cfg"); +pref("general.config.obscure_value", 0); diff --git a/overlay/usr/lib64/firefox/distribution/policies.json b/overlay/usr/lib64/firefox/distribution/policies.json new file mode 100644 index 0000000..04e4781 --- /dev/null +++ b/overlay/usr/lib64/firefox/distribution/policies.json @@ -0,0 +1,10 @@ +{ + "policies": { + "BlockAboutConfig": true, + "Extensions": { + "Install": [ + "https://addons.mozilla.org/firefox/downloads/file/1105247/auto_fullscreen-1.0.0.2-fx.xpi" + ] + } + } +} diff --git a/overlay/usr/lib64/firefox/firefox.cfg b/overlay/usr/lib64/firefox/firefox.cfg new file mode 100644 index 0000000..56e5dfc --- /dev/null +++ b/overlay/usr/lib64/firefox/firefox.cfg @@ -0,0 +1,2 @@ +// Load preferences from remote +pref("autoadmin.global_config_url","http://rosalina.pyrocufflink.blue/~dustin/autoconfigfile.js"); diff --git a/overlay/usr/local/libexec/home-overlay.sh b/overlay/usr/local/libexec/home-overlay.sh new file mode 100755 index 0000000..9e112f1 --- /dev/null +++ b/overlay/usr/local/libexec/home-overlay.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +if mountpoint -q "${HOME}"; then + exit 0 +fi + +upperdir="${XDG_RUNTIME_DIR}"/home/rw +workdir="${XDG_RUNTIME_DIR}"/home/.work + +mkdir -p "${upperdir}" +mkdir -p "${workdir}" + +mount overlay \ + -t overlay \ + -o lowerdir="${HOME}",upperdir="${upperdir}",workdir="${workdir}" \ + "${HOME}" +chown "${PAM_USER}": "${HOME}" diff --git a/prepare.sh b/prepare.sh new file mode 100644 index 0000000..80eb9bc --- /dev/null +++ b/prepare.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +shopt -s nullglob + +dnf install -y \ + make \ + rsync \ + squashfs-tools \ + tar \ + unzip \ + util-linux \ + -- diff --git a/publish.sh b/publish.sh new file mode 100644 index 0000000..3d16903 --- /dev/null +++ b/publish.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu + +destdir="$(readlink -e "$1")" +bootdir="${destdir}/boot" + +rsync -rtliO --chmod=a=rwX \ + "${bootdir}"/efi/ \ + "${bootdir}"/initramfs.img \ + "${bootdir}"/vmlinuz \ + pxe0.pyrocufflink.blue:/var/lib/tftpboot/basementhud/ +rsync -ti --chmod=a=rwX --no-W \ + "${bootdir}"/basementhud.squashfs \ + pxe0.pyrocufflink.blue:/var/lib/nbd/basementhud.squashfs diff --git a/squashfs-exclude.txt b/squashfs-exclude.txt new file mode 100644 index 0000000..c2c1915 --- /dev/null +++ b/squashfs-exclude.txt @@ -0,0 +1,70 @@ +boot/* +dev/* +etc/dnf* +etc/pki/CA +etc/pki/rpm-gpg +etc/pki/tls/certs/Makefile +etc/yum* +proc/* +run/* +sys/* +tmp/* +usr/bin/as +usr/bin/c++filt +usr/bin/dwp +usr/bin/elfedit +usr/bin/gmake +usr/bin/gprof +usr/bin/ld +usr/bin/ld.* +usr/bin/make +usr/bin/makedb +usr/bin/objcopy +usr/bin/objdump +usr/bin/perl* +usr/bin/pip* +usr/bin/ranlib +usr/bin/rpm* +usr/bin/size +usr/bin/strings +usr/games +usr/lib/dracut +usr/lib/games +usr/lib/modules/*/.vmlinuz*.hmac +usr/lib/modules/*/System.map* +usr/lib/modules/*/config* +usr/lib/modules/*/dtb +usr/lib/modules/*/initramfs* +usr/lib/modules/*/symvers* +usr/lib/modules/*/vmlinuz* +usr/lib/pkgconfig +usr/lib64/games +usr/lib64/pkgconfig +usr/share/GeoIP +usr/share/applications +usr/share/bash-completions/completions/pip* +usr/share/doc +usr/share/games +usr/share/info +usr/share/locale/*/*/bfd.mo +usr/share/locale/*/*/gas.mo +usr/share/locale/*/*/gold.mo +usr/share/locale/*/*/gprof.mo +usr/share/locale/*/*/ld.mo +usr/share/locale/*/*/make.mo +usr/share/man +usr/share/pkgconfig +usr/src +var/cache/dnf +var/cache/yum +var/games +var/lib/Makefile +var/lib/dnf +var/lib/games +var/lib/rpm +var/lib/yum +var/local +var/log/yum.log +var/nis +var/opt +var/yp