Fix: Enforce effective repository trust preflight
This commit is contained in:
@@ -105,6 +105,23 @@ record_packages_hash() {
|
|||||||
hash_packages >"$PACKAGES_HASH"
|
hash_packages >"$PACKAGES_HASH"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Repository trust is checked before the installer can reach its bootstrap DNF.
|
||||||
|
# Status 78 is reserved for a trust-root failure and is propagated unchanged so
|
||||||
|
# no later stage, especially install-hardware, can invoke DNF with that repo.
|
||||||
|
TERRA_TRUST_FAILURE_STATUS=78
|
||||||
|
trust_preflight="$PANAMA_PATH/setup/scripts/install-packages"
|
||||||
|
if [[ ! -x "$trust_preflight" ]]; then
|
||||||
|
printf 'install: package repository trust preflight is unavailable\n' >&2
|
||||||
|
exit "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
|
fi
|
||||||
|
if "$trust_preflight" --trust-preflight; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
trust_status=$?
|
||||||
|
printf 'install: package repository trust preflight failed\n' >&2
|
||||||
|
exit "$trust_status"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── The interview ────────────────────────────────────────────────────────────
|
# ── The interview ────────────────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
# Everything Panama needs to be told is asked here, before a single package is
|
# Everything Panama needs to be told is asked here, before a single package is
|
||||||
@@ -294,11 +311,18 @@ for stage in "${STAGES[@]}"; do
|
|||||||
echo "Run with --packages to install them anyway."
|
echo "Run with --packages to install them anyway."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if ! "$script"; then
|
if "$script"; then
|
||||||
|
if [[ "$stage" == install-packages ]]; then
|
||||||
|
record_packages_hash
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
stage_status=$?
|
||||||
|
if [[ "$stage" == install-packages && "$stage_status" -eq "$TERRA_TRUST_FAILURE_STATUS" ]]; then
|
||||||
|
printf '!!! %s stopped on an untrusted package repository\n' "$stage" >&2
|
||||||
|
exit "$stage_status"
|
||||||
|
fi
|
||||||
failed+=("$stage")
|
failed+=("$stage")
|
||||||
printf '!!! %s failed\n' "$stage" >&2
|
printf '!!! %s failed\n' "$stage" >&2
|
||||||
elif [[ "$stage" == install-packages ]]; then
|
|
||||||
record_packages_hash
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
+162
-23
@@ -294,6 +294,36 @@ _ini_section_count() {
|
|||||||
' "$file"
|
' "$file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ini_key_occurrence_count() {
|
||||||
|
local file="$1" wanted_section="$2" wanted_key="$3"
|
||||||
|
awk -v wanted_section="$wanted_section" -v wanted_key="$wanted_key" '
|
||||||
|
function trim(value) {
|
||||||
|
sub(/^[[:space:]]+/, "", value)
|
||||||
|
sub(/[[:space:]]+$/, "", value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
{
|
||||||
|
sub(/\r$/, "")
|
||||||
|
line = trim($0)
|
||||||
|
if (line == "" || line ~ /^[#;]/) next
|
||||||
|
if (line ~ /^\[[^]]+\]$/) {
|
||||||
|
section = substr(line, 2, length(line) - 2)
|
||||||
|
next
|
||||||
|
}
|
||||||
|
if (tolower(section) != tolower(wanted_section)) next
|
||||||
|
equals = index(line, "=")
|
||||||
|
if (equals > 0) {
|
||||||
|
key = trim(substr(line, 1, equals - 1))
|
||||||
|
} else {
|
||||||
|
split(line, words, /[[:space:]]+/)
|
||||||
|
key = words[1]
|
||||||
|
}
|
||||||
|
if (tolower(key) == tolower(wanted_key)) count++
|
||||||
|
}
|
||||||
|
END { print count + 0 }
|
||||||
|
' "$file"
|
||||||
|
}
|
||||||
|
|
||||||
_restore_repository_file() {
|
_restore_repository_file() {
|
||||||
local existed="$1" backup="$2" mode="$3" destination="$4"
|
local existed="$1" backup="$2" mode="$3" destination="$4"
|
||||||
if (( existed )); then
|
if (( existed )); then
|
||||||
@@ -346,29 +376,107 @@ _publish_repository_pair() {
|
|||||||
return "$status"
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
_terra_repository_is_trusted() {
|
_effective_terra_key() {
|
||||||
local repo_file baseurl enabled gpgcheck repo_gpgcheck gpgkey local_key
|
awk -v reviewed_baseurl="${INSTALLER_PROVENANCE[TERRA_BASEURL]}" '
|
||||||
repo_file="$PANAMA_SYSTEM_ETC/yum.repos.d/terra.repo"
|
function reset_block() {
|
||||||
[[ -f "$repo_file" ]] || return 1
|
delete values
|
||||||
baseurl="$(_ini_value "$repo_file" terra baseurl)" || return 1
|
delete seen
|
||||||
enabled="$(_ini_value "$repo_file" terra enabled)" || return 1
|
in_block = 0
|
||||||
gpgcheck="$(_ini_value "$repo_file" terra gpgcheck)" || return 1
|
id = ""
|
||||||
repo_gpgcheck="$(_ini_value "$repo_file" terra repo_gpgcheck)" || return 1
|
}
|
||||||
gpgkey="$(_ini_value "$repo_file" terra gpgkey)" || return 1
|
function finish_block( key) {
|
||||||
[[ "$baseurl" == "${INSTALLER_PROVENANCE[TERRA_BASEURL]}" \
|
if (!in_block) return
|
||||||
&& "$enabled" == 1 && "$gpgcheck" == 1 && "$repo_gpgcheck" == 1 \
|
if (seen["enabled"] != 1) {
|
||||||
&& "$gpgkey" == 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama' ]] || return 1
|
bad = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (values["enabled"] != "1") return
|
||||||
|
enabled_count++
|
||||||
|
if (id != "terra") bad = 1
|
||||||
|
for (key in required) {
|
||||||
|
if (seen[key] != 1) bad = 1
|
||||||
|
}
|
||||||
|
if (values["baseurl"] != reviewed_baseurl || values["metalink"] != "" \
|
||||||
|
|| values["mirrorlist"] != "" || values["gpgcheck"] != "1" \
|
||||||
|
|| values["pkg_gpgcheck"] != "1" || values["repo_gpgcheck"] != "1" \
|
||||||
|
|| values["gpgkey"] != "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama") bad = 1
|
||||||
|
trusted_key = values["gpgkey"]
|
||||||
|
}
|
||||||
|
BEGIN {
|
||||||
|
split("enabled baseurl metalink mirrorlist gpgcheck pkg_gpgcheck repo_gpgcheck gpgkey", fields)
|
||||||
|
for (field_index in fields) required[fields[field_index]] = 1
|
||||||
|
reset_block()
|
||||||
|
}
|
||||||
|
/^======== ".*" repository configuration: ========$/ {
|
||||||
|
finish_block()
|
||||||
|
reset_block()
|
||||||
|
id = $0
|
||||||
|
sub(/^======== "/, "", id)
|
||||||
|
sub(/" repository configuration: ========$/, "", id)
|
||||||
|
if (id == "" || id !~ /^terra/) bad = 1
|
||||||
|
in_block = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if (!in_block || $0 == "") next
|
||||||
|
separator = index($0, " = ")
|
||||||
|
if (separator > 0) {
|
||||||
|
key = substr($0, 1, separator - 1)
|
||||||
|
if (key in required) {
|
||||||
|
seen[key]++
|
||||||
|
values[key] = substr($0, separator + 3)
|
||||||
|
}
|
||||||
|
} else if ($0 == "mirrorlist") {
|
||||||
|
seen["mirrorlist"]++
|
||||||
|
values["mirrorlist"] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
finish_block()
|
||||||
|
if (bad || enabled_count > 1) exit 2
|
||||||
|
if (enabled_count == 0) exit 1
|
||||||
|
print trusted_key
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Status 0 is one trusted effective Terra identity, 1 is no enabled Terra
|
||||||
|
# identity, and 2 is an unsafe, duplicated, or unreadable effective state.
|
||||||
|
_terra_effective_status() {
|
||||||
|
local dump gpgkey parse_status=0 local_key
|
||||||
|
dump="$(dnf --quiet --no-plugins --dump-repo-config='terra*')" || return 2
|
||||||
|
gpgkey="$(printf '%s\n' "$dump" | _effective_terra_key)" || parse_status=$?
|
||||||
|
(( parse_status == 0 )) || return "$parse_status"
|
||||||
|
[[ "$gpgkey" == 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama' ]] || return 2
|
||||||
local_key="$PANAMA_SYSTEM_ETC/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama"
|
local_key="$PANAMA_SYSTEM_ETC/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama"
|
||||||
[[ -f "$local_key" ]] || return 1
|
[[ -f "$local_key" ]] || return 2
|
||||||
key_fingerprint_matches "$PANAMA_PATH/setup/provenance/keys/terra44.asc" \
|
key_fingerprint_matches "$PANAMA_PATH/setup/provenance/keys/terra44.asc" \
|
||||||
"${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" \
|
"${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" \
|
||||||
&& key_fingerprint_matches "$local_key" \
|
&& key_fingerprint_matches "$local_key" \
|
||||||
"${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}"
|
"${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" \
|
||||||
|
|| return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
TERRA_TRUST_FAILURE_STATUS=78
|
||||||
|
|
||||||
|
preflight_terra_trust() {
|
||||||
|
local status=0
|
||||||
|
_require_policy_value TERRA_BASEURL 'https://repos.fyralabs.com/terra44' \
|
||||||
|
|| return "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
|
_require_policy_value TERRA_FINGERPRINT AE09157A4DE88B497EA1D5D300CDAB43DE226D6F \
|
||||||
|
|| return "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
|
_terra_effective_status || status=$?
|
||||||
|
if (( status == 0 || status == 1 )); then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log "Effective Terra repository configuration is not trusted; refusing all package work"
|
||||||
|
return "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Status 0 is trusted, 1 is absent, and 2 is present but untrusted or malformed.
|
# Status 0 is trusted, 1 is absent, and 2 is present but untrusted or malformed.
|
||||||
_flathub_remote_status() {
|
_flathub_remote_status() {
|
||||||
local config section_count url gpg_verify summary_verify disabled disabled_status
|
local config section_count url gpg_verify summary_verify disabled disabled_status
|
||||||
|
local alternate_key_count
|
||||||
config="$PANAMA_SYSTEM_FLATPAK_REPO/config"
|
config="$PANAMA_SYSTEM_FLATPAK_REPO/config"
|
||||||
[[ -f "$config" ]] || return 1
|
[[ -f "$config" ]] || return 1
|
||||||
section_count="$(_ini_section_count "$config" 'remote "flathub"')" || return 2
|
section_count="$(_ini_section_count "$config" 'remote "flathub"')" || return 2
|
||||||
@@ -387,6 +495,11 @@ _flathub_remote_status() {
|
|||||||
elif (( disabled_status != 1 )); then
|
elif (( disabled_status != 1 )); then
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
alternate_key_count="$(_ini_key_occurrence_count "$config" 'remote "flathub"' gpgkeypath)" \
|
||||||
|
|| return 2
|
||||||
|
# The reviewed default keyring is the only permitted trust source. Empty,
|
||||||
|
# duplicate, malformed, and nonempty alternate paths all fail closed.
|
||||||
|
(( alternate_key_count == 0 )) || return 2
|
||||||
[[ -f "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" ]] || return 2
|
[[ -f "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" ]] || return 2
|
||||||
key_fingerprint_matches "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" \
|
key_fingerprint_matches "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" \
|
||||||
"${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}" || return 2
|
"${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}" || return 2
|
||||||
@@ -428,17 +541,21 @@ install_rpmfusion_repositories() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_terra_repository() {
|
install_terra_repository() {
|
||||||
local work staged_key staged_repo status
|
local work staged_key staged_repo status effective_status=0
|
||||||
require_reviewed_fedora_release || return 1
|
require_reviewed_fedora_release || return 1
|
||||||
_require_policy_value TERRA_BASEURL 'https://repos.fyralabs.com/terra44' || return 1
|
_require_policy_value TERRA_BASEURL 'https://repos.fyralabs.com/terra44' || return 1
|
||||||
_require_policy_value TERRA_FINGERPRINT AE09157A4DE88B497EA1D5D300CDAB43DE226D6F || return 1
|
_require_policy_value TERRA_FINGERPRINT AE09157A4DE88B497EA1D5D300CDAB43DE226D6F || return 1
|
||||||
if rpm -q terra-release >/dev/null 2>&1; then
|
_terra_effective_status || effective_status=$?
|
||||||
if _terra_repository_is_trusted; then
|
if (( effective_status == 0 )); then
|
||||||
log "Terra repository already installed and verified"
|
log "Terra repository already configured and verified"
|
||||||
return 0
|
return 0
|
||||||
|
elif (( effective_status != 1 )); then
|
||||||
|
log "Effective Terra repository configuration is not trusted"
|
||||||
|
return "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
fi
|
fi
|
||||||
log "Installed Terra repository does not match Panama's reviewed trust policy"
|
if rpm -q terra-release >/dev/null 2>&1; then
|
||||||
return 1
|
log "terra-release is installed without one trusted enabled Terra repository"
|
||||||
|
return "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
fi
|
fi
|
||||||
work="$(mktemp -d)" || return 1
|
work="$(mktemp -d)" || return 1
|
||||||
chmod 0700 "$work"
|
chmod 0700 "$work"
|
||||||
@@ -472,8 +589,10 @@ install_terra_repository() {
|
|||||||
if (( status == 0 )); then
|
if (( status == 0 )); then
|
||||||
sudo install -m 0644 "$staged_repo" /etc/yum.repos.d/terra.repo || status=$?
|
sudo install -m 0644 "$staged_repo" /etc/yum.repos.d/terra.repo || status=$?
|
||||||
fi
|
fi
|
||||||
if (( status == 0 )) && ! _terra_repository_is_trusted; then
|
if (( status == 0 )); then
|
||||||
status=1
|
effective_status=0
|
||||||
|
_terra_effective_status || effective_status=$?
|
||||||
|
(( effective_status == 0 )) || status="$TERRA_TRUST_FAILURE_STATUS"
|
||||||
fi
|
fi
|
||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
return "$status"
|
return "$status"
|
||||||
@@ -513,7 +632,7 @@ configure_hyprland_repository() {
|
|||||||
|
|
||||||
ensure_flathub_remote() {
|
ensure_flathub_remote() {
|
||||||
local work descriptor encoded key_file url no_gpg_verify gpg_verify
|
local work descriptor encoded key_file url no_gpg_verify gpg_verify
|
||||||
local status remote_status no_gpg_status gpg_status
|
local alternate_key_count status remote_status no_gpg_status gpg_status
|
||||||
require_reviewed_fedora_release || return 1
|
require_reviewed_fedora_release || return 1
|
||||||
_require_policy_value FLATHUB_DESCRIPTOR_URL 'https://flathub.org/repo/flathub.flatpakrepo' || return 1
|
_require_policy_value FLATHUB_DESCRIPTOR_URL 'https://flathub.org/repo/flathub.flatpakrepo' || return 1
|
||||||
_require_policy_value FLATHUB_DESCRIPTOR_MAX_BYTES 1048576 || return 1
|
_require_policy_value FLATHUB_DESCRIPTOR_MAX_BYTES 1048576 || return 1
|
||||||
@@ -556,6 +675,12 @@ ensure_flathub_remote() {
|
|||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
alternate_key_count="$(_ini_key_occurrence_count "$descriptor" 'Flatpak Repo' GPGKeyPath)" \
|
||||||
|
|| alternate_key_count=1
|
||||||
|
if (( alternate_key_count != 0 )); then
|
||||||
|
rm -rf -- "$work"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
if ! key_fingerprint_matches "$key_file" "${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}"; then
|
if ! key_fingerprint_matches "$key_file" "${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}"; then
|
||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
return 1
|
return 1
|
||||||
@@ -648,6 +773,20 @@ install_claude_desktop_if_trusted() {
|
|||||||
# RPM Fusion, no Terra, no COPR, no multimedia, no flatpaks: those exist for a
|
# RPM Fusion, no Terra, no COPR, no multimedia, no flatpaks: those exist for a
|
||||||
# desktop, and every one of them is a network dependency and a failure mode a
|
# desktop, and every one of them is a network dependency and a failure mode a
|
||||||
# headless machine has no reason to carry.
|
# headless machine has no reason to carry.
|
||||||
|
if [[ "${1:-}" == --trust-preflight ]]; then
|
||||||
|
if preflight_terra_trust; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Repeat the enclosing installer's early preflight at the package boundary so
|
||||||
|
# a repository change made after startup cannot reach this stage's first DNF.
|
||||||
|
if ! preflight_terra_trust; then
|
||||||
|
exit "$TERRA_TRUST_FAILURE_STATUS"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$ROLE" == server ]]; then
|
if [[ "$ROLE" == server ]]; then
|
||||||
echo -e "\n--- Installing packages (server) ---"
|
echo -e "\n--- Installing packages (server) ---"
|
||||||
log "Updating all packages. This may take a while"
|
log "Updating all packages. This may take a while"
|
||||||
|
|||||||
@@ -445,6 +445,71 @@ STUB
|
|||||||
|
|
||||||
cat > "$case_root/bin/dnf" <<'STUB'
|
cat > "$case_root/bin/dnf" <<'STUB'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
if [[ "$*" == '--quiet --no-plugins --dump-repo-config=terra*' ]]; then
|
||||||
|
printf 'dnf:dump-terra\n' >> "$COMMAND_LOG"
|
||||||
|
mode="${STUB_TERRA_EFFECTIVE_MODE:-auto}"
|
||||||
|
if [[ "$mode" == auto ]]; then
|
||||||
|
case "${STUB_TERRA_REPO_MODE:-absent}" in
|
||||||
|
trusted|wrong-key) mode=trusted ;;
|
||||||
|
nogpg) mode=legacy ;;
|
||||||
|
wrong-url) mode=override-url ;;
|
||||||
|
esac
|
||||||
|
if [[ "$mode" == auto && -f "$STUB_ETC/yum.repos.d/terra.repo" ]] \
|
||||||
|
&& grep -q '^baseurl=https://repos.fyralabs.com/terra44$' "$STUB_ETC/yum.repos.d/terra.repo"; then
|
||||||
|
mode=trusted
|
||||||
|
elif [[ "$mode" == auto ]]; then
|
||||||
|
mode=absent
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
case "$mode" in
|
||||||
|
absent) exit 0 ;;
|
||||||
|
trusted)
|
||||||
|
printf '======== "terra" repository configuration: ========\n'
|
||||||
|
printf 'baseurl = https://repos.fyralabs.com/terra44\n'
|
||||||
|
printf 'enabled = 1\n'
|
||||||
|
printf 'gpgcheck = 1\n'
|
||||||
|
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
|
||||||
|
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
|
||||||
|
;;
|
||||||
|
legacy)
|
||||||
|
printf '======== "terra" repository configuration: ========\n'
|
||||||
|
printf 'baseurl = \nenabled = 1\ngpgcheck = 0\n'
|
||||||
|
printf 'gpgkey = https://repos.fyralabs.com/terra44.key\n'
|
||||||
|
printf 'metalink = https://tetsudou.fyralabs.com/terra44\nmirrorlist = \n'
|
||||||
|
printf 'pkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
|
||||||
|
;;
|
||||||
|
override-url)
|
||||||
|
printf '======== "terra" repository configuration: ========\n'
|
||||||
|
printf 'baseurl = https://evil.invalid/terra44\nenabled = 1\ngpgcheck = 1\n'
|
||||||
|
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
|
||||||
|
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
|
||||||
|
;;
|
||||||
|
override-gpg)
|
||||||
|
printf '======== "terra" repository configuration: ========\n'
|
||||||
|
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 0\n'
|
||||||
|
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
|
||||||
|
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
|
||||||
|
;;
|
||||||
|
duplicate)
|
||||||
|
for id in terra terra; do
|
||||||
|
printf '======== "%s" repository configuration: ========\n' "$id"
|
||||||
|
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
|
||||||
|
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
|
||||||
|
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
alternate)
|
||||||
|
for id in terra terra-legacy; do
|
||||||
|
printf '======== "%s" repository configuration: ========\n' "$id"
|
||||||
|
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
|
||||||
|
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
|
||||||
|
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
exit 69
|
exit 69
|
||||||
STUB
|
STUB
|
||||||
|
|
||||||
@@ -537,6 +602,23 @@ run_installer_function() {
|
|||||||
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
|
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
|
||||||
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
|
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
|
||||||
;;
|
;;
|
||||||
|
alternate-key)
|
||||||
|
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\ngpgkeypath=/unreviewed/keyring.gpg\n' \
|
||||||
|
> "$case_root/flatpak-repo/config"
|
||||||
|
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
|
||||||
|
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
|
||||||
|
;;
|
||||||
|
empty-alternate-key|duplicate-alternate-key|malformed-alternate-key)
|
||||||
|
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\n' \
|
||||||
|
> "$case_root/flatpak-repo/config"
|
||||||
|
case "$STUB_FLATPAK_REMOTE_MODE" in
|
||||||
|
empty-alternate-key) printf 'gpgkeypath=\n' ;;
|
||||||
|
duplicate-alternate-key) printf 'gpgkeypath=\ngpgkeypath=/unreviewed/keyring.gpg\n' ;;
|
||||||
|
malformed-alternate-key) printf 'gpgkeypath /unreviewed/keyring.gpg\n' ;;
|
||||||
|
esac >> "$case_root/flatpak-repo/config"
|
||||||
|
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
|
||||||
|
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
case "${STUB_CLAUDE_DESKTOP_REPO_MODE:-absent}" in
|
case "${STUB_CLAUDE_DESKTOP_REPO_MODE:-absent}" in
|
||||||
trusted)
|
trusted)
|
||||||
@@ -602,11 +684,13 @@ reset_installer_fixture
|
|||||||
expect_success run_installer_function terra install_terra_repository
|
expect_success run_installer_function terra install_terra_repository
|
||||||
assert_log terra "$(cat <<'EXPECTED'
|
assert_log terra "$(cat <<'EXPECTED'
|
||||||
rpm:release
|
rpm:release
|
||||||
|
dnf:dump-terra
|
||||||
rpm:query:terra-release
|
rpm:query:terra-release
|
||||||
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
sudo:install:terra44.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama
|
sudo:install:terra44.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama
|
||||||
sudo:dnf install -y --repofrompath terra,https://repos.fyralabs.com/terra44 --setopt=terra.pkg_gpgcheck=1 --setopt=terra.repo_gpgcheck=1 --setopt=terra.gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama terra-release
|
sudo:dnf install -y --repofrompath terra,https://repos.fyralabs.com/terra44 --setopt=terra.pkg_gpgcheck=1 --setopt=terra.repo_gpgcheck=1 --setopt=terra.gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama terra-release
|
||||||
sudo:install:terra.repo:/etc/yum.repos.d/terra.repo
|
sudo:install:terra.repo:/etc/yum.repos.d/terra.repo
|
||||||
|
dnf:dump-terra
|
||||||
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
EXPECTED
|
EXPECTED
|
||||||
@@ -713,7 +797,8 @@ EXPECTED
|
|||||||
)"
|
)"
|
||||||
assert_file_bytes "$test_tmp/cases/flathub-existing-trusted/flatpak-state" 'preserved'
|
assert_file_bytes "$test_tmp/cases/flathub-existing-trusted/flatpak-state" 'preserved'
|
||||||
|
|
||||||
for mode in wrong-url wrong-key no-gpg; do
|
for mode in wrong-url wrong-key no-gpg alternate-key empty-alternate-key \
|
||||||
|
duplicate-alternate-key malformed-alternate-key; do
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
name="flathub-existing-$mode"
|
name="flathub-existing-$mode"
|
||||||
STUB_FLATPAK_REMOTE_MODE="$mode" \
|
STUB_FLATPAK_REMOTE_MODE="$mode" \
|
||||||
@@ -729,7 +814,7 @@ STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE=trusted \
|
|||||||
expect_success run_installer_function terra-existing-trusted install_terra_repository
|
expect_success run_installer_function terra-existing-trusted install_terra_repository
|
||||||
assert_log terra-existing-trusted "$(cat <<'EXPECTED'
|
assert_log terra-existing-trusted "$(cat <<'EXPECTED'
|
||||||
rpm:release
|
rpm:release
|
||||||
rpm:query:terra-release
|
dnf:dump-terra
|
||||||
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
EXPECTED
|
EXPECTED
|
||||||
@@ -748,7 +833,11 @@ done
|
|||||||
# when one copy looks safe. These cases catch the absent/duplicate conflation.
|
# when one copy looks safe. These cases catch the absent/duplicate conflation.
|
||||||
for duplicate_case in \
|
for duplicate_case in \
|
||||||
$'duplicate-no-gpg NoGPGVerify=false\nNoGPGVerify=true' \
|
$'duplicate-no-gpg NoGPGVerify=false\nNoGPGVerify=true' \
|
||||||
$'duplicate-gpg-verify GPGVerify=true\nGPGVerify=false'; do
|
$'duplicate-gpg-verify GPGVerify=true\nGPGVerify=false' \
|
||||||
|
'alternate-gpg-key-path GPGKeyPath=/unreviewed/keyring.gpg' \
|
||||||
|
'empty-gpg-key-path GPGKeyPath=' \
|
||||||
|
$'duplicate-gpg-key-path GPGKeyPath=\nGPGKeyPath=/unreviewed/keyring.gpg' \
|
||||||
|
'malformed-gpg-key-path GPGKeyPath /unreviewed/keyring.gpg'; do
|
||||||
name="${duplicate_case%% *}"
|
name="${duplicate_case%% *}"
|
||||||
lines="${duplicate_case#* }"
|
lines="${duplicate_case#* }"
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
@@ -759,6 +848,35 @@ for duplicate_case in \
|
|||||||
|| fail "$name descriptor reached remote activation"
|
|| fail "$name descriptor reached remote activation"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# DNF's non-networking effective configuration dump, rather than any one repo
|
||||||
|
# file, decides whether Terra is absent, trusted, overridden, or duplicated.
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_TERRA_EFFECTIVE_MODE=absent \
|
||||||
|
expect_success run_installer_function terra-effective-absent preflight_terra_trust
|
||||||
|
assert_log terra-effective-absent 'dnf:dump-terra'
|
||||||
|
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_TERRA_EFFECTIVE_MODE=trusted STUB_TERRA_REPO_MODE=trusted \
|
||||||
|
expect_success run_installer_function terra-effective-trusted preflight_terra_trust
|
||||||
|
assert_log terra-effective-trusted "$(cat <<'EXPECTED'
|
||||||
|
dnf:dump-terra
|
||||||
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
|
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
|
||||||
|
EXPECTED
|
||||||
|
)"
|
||||||
|
|
||||||
|
for mode in legacy override-url override-gpg duplicate alternate; do
|
||||||
|
reset_installer_fixture
|
||||||
|
name="terra-effective-$mode"
|
||||||
|
status=0
|
||||||
|
STUB_TERRA_EFFECTIVE_MODE="$mode" STUB_TERRA_REPO_MODE=trusted \
|
||||||
|
run_installer_function "$name" preflight_terra_trust || status=$?
|
||||||
|
[[ "$status" -eq 78 ]] \
|
||||||
|
|| fail "effective Terra $mode returned $status instead of hard trust status 78"
|
||||||
|
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|
||||||
|
|| fail "effective Terra $mode reached a transaction"
|
||||||
|
done
|
||||||
|
|
||||||
assert_pair_rollback() {
|
assert_pair_rollback() {
|
||||||
local name="$1" pair="$2" prior="$3" key repo
|
local name="$1" pair="$2" prior="$3" key repo
|
||||||
case "$pair" in
|
case "$pair" in
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ STAGE_NAMES=(install-packages link-dotfiles link-skills link-user change-setting
|
|||||||
# A PANAMA_PATH that looks enough like the real one for install to run, and
|
# A PANAMA_PATH that looks enough like the real one for install to run, and
|
||||||
# records what it was asked to do instead of doing it.
|
# records what it was asked to do instead of doing it.
|
||||||
build_fixture() {
|
build_fixture() {
|
||||||
local root="$1" packages_rc="${2:-0}"
|
local root="$1" packages_rc="${2:-0}" trust_rc="${3:-0}"
|
||||||
rm -rf "$root"
|
rm -rf "$root"
|
||||||
mkdir -p "$root/bin" "$root/setup/scripts" "$root/setup/packages" \
|
mkdir -p "$root/bin" "$root/setup/scripts" "$root/setup/packages" \
|
||||||
"$root/config/dot/quickshell/scripts"
|
"$root/config/dot/quickshell/scripts"
|
||||||
@@ -63,6 +63,10 @@ EOF
|
|||||||
# The one stage whose exit code the caller wants to control.
|
# The one stage whose exit code the caller wants to control.
|
||||||
cat >"$root/setup/scripts/install-packages" <<EOF
|
cat >"$root/setup/scripts/install-packages" <<EOF
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
if [[ "\${1:-}" == --trust-preflight ]]; then
|
||||||
|
printf 'trust-preflight\n' >>"\$PANAMA_RAN"
|
||||||
|
exit $trust_rc
|
||||||
|
fi
|
||||||
printf 'install-packages\n' >>"\$PANAMA_RAN"
|
printf 'install-packages\n' >>"\$PANAMA_RAN"
|
||||||
exit $packages_rc
|
exit $packages_rc
|
||||||
EOF
|
EOF
|
||||||
@@ -103,6 +107,14 @@ EOF
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
exit 0
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
|
cat >"$root/shim/dnf" <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
printf 'dnf-transaction\n' >>"$PANAMA_RAN"
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
for prerequisite in gum lspci mokutil fwupdmgr; do
|
||||||
|
ln -s gsettings "$root/shim/$prerequisite"
|
||||||
|
done
|
||||||
chmod +x "$root/shim"/*
|
chmod +x "$root/shim"/*
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +227,44 @@ run_install "$tmp/c" --upgrade >/dev/null || install_status=$?
|
|||||||
if [[ -r "$tmp/c/state/panama/packages-hash" ]]; then
|
if [[ -r "$tmp/c/state/panama/packages-hash" ]]; then
|
||||||
note 'install-packages failed but its hash was recorded, so it will never be retried'
|
note 'install-packages failed but its hash was recorded, so it will never be retried'
|
||||||
fi
|
fi
|
||||||
|
grep -qx 'link-dotfiles' "$tmp/c/ran" \
|
||||||
|
|| note 'an ordinary package-stage failure no longer allows later safe stages'
|
||||||
|
|
||||||
|
# An invalid enabled Terra root is not an ordinary package failure. It must
|
||||||
|
# stop before the installer's bootstrap DNF and before every stage.
|
||||||
|
build_fixture "$tmp/terra-preflight-hard" 0 78
|
||||||
|
install_status=0
|
||||||
|
run_install "$tmp/terra-preflight-hard" >/dev/null || install_status=$?
|
||||||
|
[[ "$install_status" -eq 78 ]] \
|
||||||
|
|| note "initial Terra trust failure returned $install_status instead of 78"
|
||||||
|
asserted_preflight="$(<"$tmp/terra-preflight-hard/ran")"
|
||||||
|
[[ "$asserted_preflight" == trust-preflight ]] \
|
||||||
|
|| note "initial Terra trust failure allowed later work: ${asserted_preflight//$'\n'/,}"
|
||||||
|
|
||||||
|
# The trust verifier is itself mandatory. Losing its executable adapter must
|
||||||
|
# fail closed before interview, bootstrap, or stage work.
|
||||||
|
build_fixture "$tmp/terra-preflight-missing"
|
||||||
|
rm "$tmp/terra-preflight-missing/setup/scripts/install-packages"
|
||||||
|
install_status=0
|
||||||
|
run_install "$tmp/terra-preflight-missing" >/dev/null || install_status=$?
|
||||||
|
[[ "$install_status" -eq 78 ]] \
|
||||||
|
|| note "missing Terra trust verifier returned $install_status instead of 78"
|
||||||
|
[[ ! -s "$tmp/terra-preflight-missing/ran" ]] \
|
||||||
|
|| note 'missing Terra trust verifier allowed later work'
|
||||||
|
|
||||||
|
# The package stage repeats the preflight to close a configuration-change race.
|
||||||
|
# Its hard status must also stop link stages and install-hardware immediately.
|
||||||
|
build_fixture "$tmp/terra-stage-hard" 78 0
|
||||||
|
install_status=0
|
||||||
|
run_install "$tmp/terra-stage-hard" >/dev/null || install_status=$?
|
||||||
|
[[ "$install_status" -eq 78 ]] \
|
||||||
|
|| note "stage-time Terra trust failure returned $install_status instead of 78"
|
||||||
|
grep -qx 'install-packages' "$tmp/terra-stage-hard/ran" \
|
||||||
|
|| note 'stage-time Terra trust fixture never reached install-packages'
|
||||||
|
for suppressed in link-dotfiles link-skills link-user change-settings install-hardware dnf-transaction; do
|
||||||
|
grep -qx "$suppressed" "$tmp/terra-stage-hard/ran" \
|
||||||
|
&& note "stage-time Terra trust failure still ran $suppressed"
|
||||||
|
done
|
||||||
|
|
||||||
# A full install always runs the stage, whatever any recorded hash says.
|
# A full install always runs the stage, whatever any recorded hash says.
|
||||||
build_fixture "$tmp/d"
|
build_fixture "$tmp/d"
|
||||||
|
|||||||
Reference in New Issue
Block a user