summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build-installer-iso.yml2
-rw-r--r--.github/workflows/validate-installer-iso.yml11
-rw-r--r--Justfile5
-rw-r--r--README.md7
-rw-r--r--installer/Containerfile6
-rwxr-xr-xinstaller/build.sh16
-rwxr-xr-xinstaller/patch-anaconda-webui.sh47
-rw-r--r--installer/system_files/overrides/etc/anaconda/conf.d/99-aerocore-password.conf5
-rwxr-xr-xjust_scripts/build-installer-iso.sh54
9 files changed, 129 insertions, 24 deletions
diff --git a/.github/workflows/build-installer-iso.yml b/.github/workflows/build-installer-iso.yml
index a6348aa..143eafc 100644
--- a/.github/workflows/build-installer-iso.yml
+++ b/.github/workflows/build-installer-iso.yml
@@ -91,6 +91,8 @@ jobs:
--build-arg "BASE_IMAGE=${LIVE_BASE_IMAGE}" \
--build-arg "INSTALL_IMAGE_PAYLOAD=${install_image_ref}:${IMAGE_TAG}" \
--build-arg "INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci" \
+ --build-arg "INSTALL_IMAGE_TAG=${IMAGE_TAG}" \
+ --build-arg "OSTREE_IMAGE_REF=${ostree_image_ref}" \
--tag localhost/aerocore-os-installer:latest \
--file installer/Containerfile \
.
diff --git a/.github/workflows/validate-installer-iso.yml b/.github/workflows/validate-installer-iso.yml
index 391afd8..a93e9cd 100644
--- a/.github/workflows/validate-installer-iso.yml
+++ b/.github/workflows/validate-installer-iso.yml
@@ -39,6 +39,17 @@ jobs:
done
bash -n system_files/usr/libexec/aerocore-instruments-setup
+ - name: Validate Anaconda WebUI patch
+ run: |
+ fixture="$(mktemp)"
+ trap 'rm -f "${fixture}"' EXIT
+ printf '%s' 'n===0?r(new Error(X2("Password is too weak"))):o(' |
+ gzip -n > "${fixture}"
+ installer/patch-anaconda-webui.sh "${fixture}"
+ gzip -dc "${fixture}" |
+ grep -Fq 'n===0&&!e?r(new Error(X2("Password is too weak"))):o('
+ installer/patch-anaconda-webui.sh "${fixture}"
+
- name: Validate installer polkit rules
run: |
for rule in installer/system_files/overrides/etc/polkit-1/rules.d/*.rules; do
diff --git a/Justfile b/Justfile
index 8161c52..47567b5 100644
--- a/Justfile
+++ b/Justfile
@@ -14,7 +14,8 @@ export VERSION_PRETTY := env_var_or_default("VERSION_PRETTY", VERSION_TAG)
export BASE_IMAGE := env_var_or_default("BASE_IMAGE", "ghcr.io/ublue-os/bazzite-deck:stable")
export OSTREE_IMAGE_REF := env_var_or_default("OSTREE_IMAGE_REF", "ostree-image-signed:docker://ghcr.io/" + repo_organization + "/" + image_name)
export LIVE_BASE_IMAGE := env_var_or_default("LIVE_BASE_IMAGE", "quay.io/fedora/fedora-kinoite:43")
-export INSTALLER_BUILDER_IMAGE := env_var_or_default("INSTALLER_BUILDER_IMAGE", "ghcr.io/jasonn3/build-container-installer:v1.5.0")
+export TITANOBOA_REPOSITORY := env_var_or_default("TITANOBOA_REPOSITORY", "https://github.com/Zeglius/titanoboa.git")
+export TITANOBOA_REVISION := env_var_or_default("TITANOBOA_REVISION", "7737f4748458252ac827dca14b3d6dd09298472a")
export FLATPAK_REMOTE_URL := env_var_or_default("FLATPAK_REMOTE_URL", "")
export HOMEBREW_BOTTLE_DOMAIN := env_var_or_default("HOMEBREW_BOTTLE_DOMAIN", "")
export HOMEBREW_API_DOMAIN := env_var_or_default("HOMEBREW_API_DOMAIN", "")
@@ -38,7 +39,7 @@ alias run-live-installer := run-installer-iso
default:
@just --list
-# Build a Bazzite-style installer ISO
+# Build the same Titanoboa-based installer ISO as CI
build-installer-iso $target_image=("localhost/" + image_name) $tag=default_tag:
@IMAGE_NAME="{{ target_image }}" IMAGE_TAG="{{ tag }}" ./just_scripts/build-installer-iso.sh
diff --git a/README.md b/README.md
index de33959..6faf2ae 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ just build-installer-iso
just run-installer-iso
```
-This requires privileged Podman, a rootful Podman API socket, and roughly
-70 GiB of free space. The installer ISO workflow consumes the published
-`latest` AeroCore image and uploads the ISO as a GitHub Actions artifact.
+This requires privileged Podman, a rootful Podman API socket, `git`, and
+roughly 70 GiB of free space. Local ISO generation uses the same pinned
+Titanoboa builder as the installer workflow, and writes the ISO plus its
+SHA-256 checksum under `output/`.
diff --git a/installer/Containerfile b/installer/Containerfile
index cf20e4f..32b327e 100644
--- a/installer/Containerfile
+++ b/installer/Containerfile
@@ -6,6 +6,8 @@ ARG IMAGE_VENDOR=AeroCore-IO
ARG IMAGE_NAME=aerocore-os
ARG INSTALL_IMAGE_PAYLOAD=ghcr.io/${IMAGE_VENDOR}/${IMAGE_NAME}:stable
ARG INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci
+ARG INSTALL_IMAGE_TAG=latest
+ARG OSTREE_IMAGE_REF=ostree-image-signed:docker://ghcr.io/${IMAGE_VENDOR}/${IMAGE_NAME}
FROM ${BASE_IMAGE}
@@ -14,10 +16,14 @@ ARG IMAGE_VENDOR
ARG IMAGE_NAME
ARG INSTALL_IMAGE_PAYLOAD
ARG INSTALL_IMAGE_PAYLOAD_ARCHIVE
+ARG INSTALL_IMAGE_TAG
+ARG OSTREE_IMAGE_REF
RUN --mount=type=bind,source=.,target=/src \
--mount=type=bind,source=output/install-payload.oci,target=/install-payload.oci,ro \
env BASE_IMAGE="${BASE_IMAGE}" \
INSTALL_IMAGE_PAYLOAD="${INSTALL_IMAGE_PAYLOAD}" \
INSTALL_IMAGE_PAYLOAD_ARCHIVE="${INSTALL_IMAGE_PAYLOAD_ARCHIVE}" \
+ INSTALL_IMAGE_TAG="${INSTALL_IMAGE_TAG}" \
+ OSTREE_IMAGE_REF="${OSTREE_IMAGE_REF}" \
/src/installer/build.sh
diff --git a/installer/build.sh b/installer/build.sh
index 0cc1b8a..4535113 100755
--- a/installer/build.sh
+++ b/installer/build.sh
@@ -6,6 +6,9 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_IMAGE=${BASE_IMAGE:?}
INSTALL_IMAGE_PAYLOAD=${INSTALL_IMAGE_PAYLOAD:?}
INSTALL_IMAGE_PAYLOAD_ARCHIVE=${INSTALL_IMAGE_PAYLOAD_ARCHIVE:-}
+INSTALL_IMAGE_TAG=${INSTALL_IMAGE_TAG:?}
+OSTREE_IMAGE_REF=${OSTREE_IMAGE_REF:?}
+ostree_origin_ref="${OSTREE_IMAGE_REF}:${INSTALL_IMAGE_TAG}"
# The installer is a live KDE environment. Keep the installed system payload
# separate: it is the AeroCore image, while BASE_IMAGE provides the live UI.
@@ -28,6 +31,11 @@ dnf --setopt=excludepkgs= reinstall -y \
grub2-efi-x64 \
grub2-efi-x64-cdboot
+# Fedora 43's anaconda-webui bundle embeds a Cockpit password helper from
+# before cockpit-project/cockpit@900c13f. Restore that upstream fix so a weak
+# pwscore result is returned as score 0 instead of rejecting the WebUI promise.
+"${SCRIPT_DIR}/patch-anaconda-webui.sh"
+
# Apply the same AeroCore overlay to the live session where possible.
if [[ -d /src/system_files ]]; then
cp -a /src/system_files/. /
@@ -124,6 +132,14 @@ df -h /var/tmp \${target_tmp}
ostreecontainer --url=${INSTALL_IMAGE_PAYLOAD} --transport=containers-storage --no-signature-verification
+# The local containers-storage deployment is intentionally unverified because
+# the payload is loaded from an OCI archive. Point future bootc updates at the
+# signed registry reference configured for the image instead.
+%post --erroronfail --log=/tmp/aerocore-origin.log
+set -eux
+sed -i 's|container-image-reference=.*|container-image-reference=${ostree_origin_ref}|' /ostree/deploy/default/deploy/*.origin
+%end
+
%post --nochroot --log=/tmp/aerocore-hostname.log
set -eux
target_root=
diff --git a/installer/patch-anaconda-webui.sh b/installer/patch-anaconda-webui.sh
new file mode 100755
index 0000000..2f61516
--- /dev/null
+++ b/installer/patch-anaconda-webui.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+bundle="${1:-/usr/share/cockpit/anaconda-webui/index.js.gz}"
+broken='n===0?r(new Error(X2("Password is too weak"))):o('
+fixed='n===0&&!e?r(new Error(X2("Password is too weak"))):o('
+fixed_sed='n===0\&\&!e?r(new Error(X2("Password is too weak"))):o('
+
+if [[ ! -f "${bundle}" ]]; then
+ echo "Anaconda WebUI bundle not found: ${bundle}" >&2
+ exit 1
+fi
+
+webui_js="$(mktemp)"
+patched_js="$(mktemp)"
+patched_bundle="$(mktemp)"
+trap 'rm -f "${webui_js}" "${patched_js}" "${patched_bundle}"' EXIT
+
+gzip -dc "${bundle}" > "${webui_js}"
+
+fixed_count="$( (grep -oF "${fixed}" "${webui_js}" || true) | wc -l | tr -d ' ')"
+if [[ "${fixed_count}" == "1" ]]; then
+ echo "Anaconda WebUI weak-password handling is already fixed"
+ exit 0
+fi
+
+broken_count="$( (grep -oF "${broken}" "${webui_js}" || true) | wc -l | tr -d ' ')"
+if [[ "${broken_count}" != "1" ]]; then
+ echo "Unexpected Anaconda WebUI password helper; refusing an unsafe patch" >&2
+ echo "Expected one weak-password rejection, found ${broken_count}" >&2
+ exit 1
+fi
+
+sed "s|${broken}|${fixed_sed}|" "${webui_js}" > "${patched_js}"
+
+fixed_count="$( (grep -oF "${fixed}" "${patched_js}" || true) | wc -l | tr -d ' ')"
+if [[ "${fixed_count}" != "1" ]] || grep -Fq "${broken}" "${patched_js}"; then
+ echo "Failed to patch Anaconda WebUI weak-password handling" >&2
+ exit 1
+fi
+
+gzip -n -9 -c "${patched_js}" > "${patched_bundle}"
+chmod 0644 "${patched_bundle}"
+mv -f "${patched_bundle}" "${bundle}"
+
+echo "Patched Anaconda WebUI weak-password handling"
diff --git a/installer/system_files/overrides/etc/anaconda/conf.d/99-aerocore-password.conf b/installer/system_files/overrides/etc/anaconda/conf.d/99-aerocore-password.conf
new file mode 100644
index 0000000..3105934
--- /dev/null
+++ b/installer/system_files/overrides/etc/anaconda/conf.d/99-aerocore-password.conf
@@ -0,0 +1,5 @@
+[User Interface]
+password_policies =
+ root (quality 1, length 6, strict)
+ user (quality 1, length 6, empty, strict)
+ luks (quality 1, length 6)
diff --git a/just_scripts/build-installer-iso.sh b/just_scripts/build-installer-iso.sh
index abd668f..0aacf43 100755
--- a/just_scripts/build-installer-iso.sh
+++ b/just_scripts/build-installer-iso.sh
@@ -8,7 +8,8 @@ cd "${project_root}"
image_name="${IMAGE_NAME:-localhost/$(just image_name)}"
image_tag="${IMAGE_TAG:-$(just generate-default-tag)}"
live_base_image="${LIVE_BASE_IMAGE:-quay.io/fedora/fedora-kinoite:43}"
-installer_builder_image="${INSTALLER_BUILDER_IMAGE:-ghcr.io/jasonn3/build-container-installer:v1.5.0}"
+titanoboa_repository="${TITANOBOA_REPOSITORY:-https://github.com/Zeglius/titanoboa.git}"
+titanoboa_revision="${TITANOBOA_REVISION:-7737f4748458252ac827dca14b3d6dd09298472a}"
source_image="${image_name}:${image_tag}"
# Keep the payload in local container storage, but use the public update
# reference as its local tag so Anaconda writes that reference as the boot
@@ -90,6 +91,8 @@ ensure_rootful_image
--build-arg "BASE_IMAGE=${live_base_image}" \
--build-arg "INSTALL_IMAGE_PAYLOAD=${payload}" \
--build-arg "INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci" \
+ --build-arg "INSTALL_IMAGE_TAG=${image_tag}" \
+ --build-arg "OSTREE_IMAGE_REF=${ostree_image_ref}" \
--tag "${payload}" \
--file installer/Containerfile \
.
@@ -100,24 +103,37 @@ else
sudo rm -f "${iso}" "${checksum}"
fi
-"${podman_cmd[@]}" run --rm --privileged \
- --volume "${api_socket}:/var/run/docker.sock" \
- --volume /var/lib/containers/storage:/var/lib/containers/storage \
- --volume "${project_root}/output:/build-container-installer/build" \
- --volume "${project_root}/installer/lorax_templates:/additional_lorax_templates:ro" \
- "${installer_builder_image}" \
- ADDITIONAL_TEMPLATES="/additional_lorax_templates/remove_root_password_prompt.tmpl /additional_lorax_templates/set_default_user.tmpl" \
- ARCH="x86_64" \
- ENABLE_CACHE_DNF="false" \
- ENABLE_CACHE_SKOPEO="false" \
- ENABLE_FLATPAK_DEPENDENCIES="false" \
- IMAGE_NAME="aerocore-os-installer" \
- IMAGE_REPO="localhost" \
- IMAGE_SRC="containers-storage:${payload}" \
- IMAGE_TAG="latest" \
- ISO_NAME="build/aerocore-os-${image_tag}-amd64.iso" \
- VARIANT="Kinoite" \
- VERSION="${FEDORA_VERSION:-44}"
+tmp_titanoboa="$(mktemp -d -t aerocore-titanoboa.XXXXXXXXXX)"
+trap 'rm -rf "${tmp_titanoboa}"' EXIT
+
+git clone --quiet --filter=blob:none --no-checkout \
+ "${titanoboa_repository}" "${tmp_titanoboa}"
+git -C "${tmp_titanoboa}" fetch --quiet origin "${titanoboa_revision}"
+git -C "${tmp_titanoboa}" checkout --quiet "${titanoboa_revision}"
+
+# Keep this invocation aligned with the pinned revision's GitHub Action.
+# This revision exposes main.sh rather than a Justfile.
+(
+ cd "${tmp_titanoboa}"
+ env \
+ CI=1 \
+ TITANOBOA_CTR_IMAGE="${payload}" \
+ TITANOBOA_OUTPUT_DIR="${tmp_titanoboa}/output" \
+ "${tmp_titanoboa}/main.sh"
+)
+
+titanoboa_iso="$(find "${tmp_titanoboa}/output" -maxdepth 1 -type f -name '*.iso' -print -quit)"
+if [[ ! -f "${titanoboa_iso}" ]]; then
+ echo "Titanoboa did not produce an ISO in ${tmp_titanoboa}/output" >&2
+ exit 1
+fi
+if [[ "${EUID}" -eq 0 ]]; then
+ rm -f "${iso}" "${checksum}"
+ mv "${titanoboa_iso}" "${iso}"
+else
+ sudo rm -f "${iso}" "${checksum}"
+ sudo mv "${titanoboa_iso}" "${iso}"
+fi
if [[ -f "${iso}" ]]; then
if [[ "${EUID}" -ne 0 ]]; then