Merge branch 'main' into codex/repo-audit-remediation-package-2
# Conflicts: # README.md # setup/scripts/install-packages
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Installing OpenAI's ChatGPT Desktop without trusting the download. Sourced,
|
||||
# not run.
|
||||
#
|
||||
# OpenAI signs both its packages and its repository metadata, with one key, and
|
||||
# publishes neither that key nor its fingerprint anywhere a first install could
|
||||
# fetch them. The documented instructions are "download this RPM and install
|
||||
# it" -- and the RPM's own root scriptlet is what writes the repository file and
|
||||
# drops the key into /etc/pki/rpm-gpg. Following them means handing an
|
||||
# unverified download to root and letting it decide afterwards what to trust,
|
||||
# which is the one thing this repository will not do with a network response.
|
||||
#
|
||||
# So the key is pinned here instead. setup/keys/ carries a copy and records
|
||||
# where it came from; this verifies that copy's fingerprint, installs it, and
|
||||
# writes the repository itself with gpgcheck on. dnf then checks the metadata
|
||||
# signature and the package signature against that key before anything runs as
|
||||
# root, and every later upgrade goes through the same repository and the same
|
||||
# key.
|
||||
#
|
||||
# Two callers, which is why this is a library: install-packages, for a machine
|
||||
# being built, and the migration that replaces the community codex-desktop
|
||||
# build on machines that predate the official package.
|
||||
|
||||
# The key that signs the packages and the repository metadata. Pinned, so a
|
||||
# substituted key is a failure here rather than a silent change of publisher.
|
||||
CHATGPT_KEY_FINGERPRINT="3BFA0E4AE8B8CC16A2D9BA684A3B4A566C4660E4"
|
||||
|
||||
# `$basearch` stays literal: dnf expands it, and this is the same base URL the
|
||||
# package's own scriptlet configures.
|
||||
CHATGPT_REPO_BASEURL="https://persistent.oaistatic.com/codex-app-prod/linux/rpm/\$basearch"
|
||||
CHATGPT_REPO_FILE="/etc/yum.repos.d/chatgpt.repo"
|
||||
CHATGPT_KEY_FILE="/etc/pki/rpm-gpg/RPM-GPG-KEY-chatgpt"
|
||||
|
||||
chatgpt_pinned_key() {
|
||||
printf '%s/setup/keys/RPM-GPG-KEY-chatgpt' "${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
||||
}
|
||||
|
||||
# The fingerprint of the pinned copy. Nonzero when it cannot be read at all,
|
||||
# which the caller reports differently from a key that reads but is the wrong
|
||||
# one.
|
||||
chatgpt_pinned_fingerprint() {
|
||||
local key
|
||||
key="$(chatgpt_pinned_key)"
|
||||
[[ -r "$key" ]] || return 1
|
||||
gpg --show-keys --with-colons "$key" 2>/dev/null \
|
||||
| awk -F: '$1 == "fpr" { print $10; exit }'
|
||||
}
|
||||
|
||||
# Fails without touching anything when the pinned key is missing, unreadable,
|
||||
# or not the key this repository says it is. Everything below assumes it passed.
|
||||
chatgpt_verify_pinned_key() {
|
||||
local found
|
||||
if ! command -v gpg >/dev/null 2>&1; then
|
||||
printf 'gpg is missing, so the pinned ChatGPT signing key cannot be verified.\n' >&2
|
||||
return 1
|
||||
fi
|
||||
if ! found="$(chatgpt_pinned_fingerprint)"; then
|
||||
printf 'The pinned ChatGPT signing key is missing: %s\n' "$(chatgpt_pinned_key)" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$found" != "$CHATGPT_KEY_FINGERPRINT" ]]; then
|
||||
printf 'The pinned ChatGPT signing key is %s, not the expected %s.\n' \
|
||||
"${found:-unreadable}" "$CHATGPT_KEY_FINGERPRINT" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Installs the verified key and the repository that names it, so the install
|
||||
# after this one is a signature check rather than an act of faith.
|
||||
#
|
||||
# Takes the command that gets root, because the two callers ask for it
|
||||
# differently: plain `sudo` from the installer, which authenticated once at the
|
||||
# top of the run, and `panama-sudo --reason ...` from a migration, whose prompt
|
||||
# has to say which repair it is for.
|
||||
chatgpt_install_repository() {
|
||||
local -a sudo_cmd=("$@")
|
||||
(( ${#sudo_cmd[@]} > 0 )) || sudo_cmd=(sudo)
|
||||
|
||||
chatgpt_verify_pinned_key || return 1
|
||||
|
||||
"${sudo_cmd[@]}" install -D -m 0644 "$(chatgpt_pinned_key)" "$CHATGPT_KEY_FILE" || return 1
|
||||
"${sudo_cmd[@]}" rpmkeys --import "$CHATGPT_KEY_FILE" || return 1
|
||||
|
||||
# Written here rather than left to the package's scriptlet, because the
|
||||
# point of it is to exist -- with gpgcheck on and this key named -- before
|
||||
# the first install rather than after it. Same base URL and same key the
|
||||
# scriptlet writes, so it finds nothing to change later.
|
||||
printf '%s\n' \
|
||||
'[openai-chatgpt]' \
|
||||
'name=ChatGPT' \
|
||||
"baseurl=$CHATGPT_REPO_BASEURL" \
|
||||
'enabled=1' \
|
||||
'type=rpm-md' \
|
||||
'gpgcheck=1' \
|
||||
'repo_gpgcheck=1' \
|
||||
"gpgkey=file://$CHATGPT_KEY_FILE" \
|
||||
| "${sudo_cmd[@]}" tee "$CHATGPT_REPO_FILE" >/dev/null || return 1
|
||||
}
|
||||
Reference in New Issue
Block a user