假设我全新安装了 Ubuntu 21.10,并且想从 Launchpad 添加我喜欢的 PPA。
我知道我可以使用add-apt-repository
,但它目前没有将 PPA 密钥添加到/etc/apt/trusted.gpg.d/
(或者,一般来说,添加到用户设置的适当目录中,可能/usr/local/share/keyrings/
是)。将密钥放入/etc/apt/trusted.gpg.d/
将是未来的标准,Ubuntu 21.10 已经支持它。
因此,我想编写一个 bash 脚本,解析用户已从备份复制到的 *.list 文件/etc/apt/sources.list.d
,获取密钥并将它们复制到/usr/local/share/keyrings/
如果我知道表单中的 PPA 名称,那么哪种方法是获取密钥文件的巧妙方法ppa:${PPA_NAME}/${PKG_NAME}
?
我的第一个想法是:
- 了解
${PPA_NAME}
并${PKG_NAME}
转到正确的 launchpad.net 网页 - 解析网页获取指纹
- 使用指纹从下载密钥文件https://keyserver.ubuntu.com/
- 生成正确格式的密钥并将其放在正确的路径下(由
signed-by
*.list文件中的选项引用)
另一种方法可能是运行静默apt update
,捕获可能的 NO_PUBKEY 错误并使用它来检索密钥文件。
有没有不那么复杂的方法来做到这一点?
答案1
我会尽力节省您的一些时间。
为了满足我的个人需要,我创建了特殊的Python 脚本命名srslsud
(Ubuntu 和 Debian 的保存/恢复软件列表脚本)。它将保存所有 APT 存储库、它们的 GPG 密钥;Snap、Flatpaks 和 Ubuntu Make 应用程序列表到第一台机器的 JSON 文件中。然后,您可以在第二台机器上使用相同的 JSON 文件恢复此列表。
对于您来说,您可以在旧机器上部分使用其功能:
sudo apt-get install python3 python3-gi python3-apt software-properties-common python3-jsonpickle
cd ~/Downloads
wget https://raw.githubusercontent.com/N0rbert/srslsud/master/srslsud.py
chmod +x srslsud.py
./srslsud.py apt_save
./srslsud.py apt_load
cat apt.sh | grep apt-key
然后解析上述输出以获取实际的密钥。
但为了获得最佳生产力,我建议sudo bash ./apt.sh
在新机器上运行相同的 Ubuntu 版本,让它有机会完成所有工作。
即使对于 Ubuntu 22.04 LTS,此方法仍然适用,因此apt-key
弃用不是问题。
答案2
这个问题是我自己在 2021 年发布的,但最近我编写了一个 bash 脚本,该脚本以格式的 launchap ppa 的名称作为输入ppa:[ppa_user]/[ppa_name]
,创建.list
文件/etc/apt/sources.list.d
并将相关的密钥环放在可配置的文件夹中。
该脚本分为两个文件,一个add-launchpad-ppa.conf
和一个add-launchpad-ppa.sh
文件。
add-launchpad-ppa.conf
文件内容:
# Set English language
LANG=en_US.UTF-8
# Color settings
COLOR_ERROR="\e[1;31m"
COLOR_OFF="\e[0m"
COLOR_OK="\e[1;32m"
COLOR_WARNING="\e[1;33m"
# Definitions
KEY_DIR="/etc/apt/keyrings"
PPA_DIR="/etc/apt/sources.list.d"
KEY_HOST="keyserver.ubuntu.com"
PPA_HOST="launchpadcontent.net"
add-launchpad-ppa.sh
文件内容:
#!/bin/bash
#-------------------------------------------------------------------------------
# Script: add-launchpad-ppa.sh
# Author: Lorenz Keel (AskUbuntu)
# Purpose: Add PGP key in apt keyring
# Usage: sudo bash <script_dir>/add-launchpad-ppa.sh ppa:[ppa_user]/[ppa_name]
#-------------------------------------------------------------------------------
# Error status variables
STATUS_OK=0
STATUS_ERROR=1
# Execute it as root user
if [ "${USER}" != root ]; then
echo -e "${COLOR_ERROR}ERROR: must be root! Exiting...${COLOR_OFF}"
exit "${STATUS_ERROR}"
fi
# Check dependencies
if [ -z "$(command -v curl)" ]; then
echo -e "${COLOR_ERROR}ERROR: unmet dependencies. Run the command:${COLOR_OFF}"
echo -e "${COLOR_ERROR}sudo apt install curl${COLOR_OFF}"
exit "${STATUS_ERROR}"
fi
# Load configuration file
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
SCRIPT_FILE="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
CONF_FILE="${SCRIPT_DIR}/${SCRIPT_FILE/.sh/.conf}"
if [ -f "${CONF_FILE}" ]; then
# shellcheck source=/dev/null
source "${CONF_FILE}"
else
echo "ERROR: missing ${CONF_FILE} file! Exiting..."
exit "${STATUS_ERROR}"
fi
# Comment code blocks
if [ -n "${BASH}" ]; then
shopt -s expand_aliases
alias BEGINCOMMENT="if [ ]; then"
alias ENDCOMMENT="fi"
fi
# Check input parameters
PPA_PREFIX="$(echo "$1" | cut -d ":" -f 1)"
PPA_USER="$(echo "$1" | cut -d "/" -f 1 | cut -d ":" -f 2)"
PPA_NAME="$(echo "$1" | cut -d "/" -f 2)"
if [ "${PPA_PREFIX}" != "ppa" ] || [ -z "${PPA_USER}" ] || [ -z "${PPA_NAME}" ]; then
echo "Usage: sudo bash ${SCRIPT_FILE} ppa:[ppa_user]/[ppa_name]"
exit "${STATUS_ERROR}"
fi
# Create list file
echo -n "→ Creating ${PPA_USER}.list in ${PPA_DIR}... "
echo "deb [arch=amd64 signed-by=${KEY_DIR}/${PPA_USER}-keyring.gpg] https://ppa.${PPA_HOST}/${PPA_USER}/${PPA_NAME}/ubuntu $(lsb_release -sc) main" \
| tee "${PPA_DIR}/${PPA_USER}".list &> /dev/null
if [ -s "${PPA_DIR}/${PPA_USER}".list ]; then
echo -e "${COLOR_OK}Done${COLOR_OFF}"
else
echo -e "${COLOR_ERROR}ERROR: file creation fail${COLOR_OFF}"
exit "${STATUS_ERROR}"
fi
# Get fingerprint and download the key
echo -n "→ Retrieving fingerprint from https://${PPA_HOST}/~${PPA_USER}/+archive/ubuntu/${PPA_NAME}... "
PPA_FINGER="$(curl -sL "https://launchpad.net/~${PPA_USER}/+archive/ubuntu/${PPA_NAME}" | sed -n -e 's/^.*key_fingerprint//p' | cut -d \" -f 3)"
SCRIPT_NAME="${SCRIPT_FILE%.*}"
USER_NAME="${SUDO_USER:-${USER}}"
TMP_DIR="$(sudo -u "${USER_NAME}" mktemp -d "${TMPDIR:-/tmp/}${SCRIPT_NAME}.XXXX")"
TMP_FILE="tmp-${PPA_USER}"
if curl -sfL "https://${KEY_HOST}/pks/lookup?op=get&search=0x${PPA_FINGER}" -o "${TMP_DIR}/${TMP_FILE}"; then
echo -e "${COLOR_OK}Done${COLOR_OFF}"
else
echo -e "${COLOR_ERROR}ERROR: download fail${COLOR_OFF}"
rm -rf "${TMP_DIR}"
exit "${STATUS_ERROR}"
fi
# Convert the keyfile
echo -n "→ Converting keyfile... "
# Remove first character (is a blank)
KEY_TYPE="$(file "${TMP_DIR}/${TMP_FILE}" | awk -F ':' '{print substr($2, 2, length($2)-1)}')"
if [ "${KEY_TYPE}" = 'PGP public key block Public-Key (old)' ]; then
gpg --batch --yes --dearmor --keyring=gnupg-ring "${TMP_DIR}/${TMP_FILE}"
mv "${TMP_DIR}/${TMP_FILE}.gpg" "${TMP_DIR}/${PPA_USER}-keyring.gpg"
echo -e "${COLOR_OK}Done${COLOR_OFF}"
elif [ "${KEY_TYPE}" = 'PGP public key block Secret-Key' ]; then
gpg --batch --yes --no-default-keyring --keyring=gnupg-ring:"${TMP_DIR}/temp-keyring.gpg" --quiet --import "${TMP_DIR}/${TMP_FILE}"
gpg --batch --yes --no-default-keyring --keyring=gnupg-ring:"${TMP_DIR}/temp-keyring.gpg" --export --output "${TMP_DIR}/${PPA_USER}-keyring.gpg"
echo -e "${COLOR_OK}Done${COLOR_OFF}"
elif [ "${KEY_TYPE}" = 'PGP/GPG key public ring (v4)' ]; then
mv "${TMP_DIR}/${TMP_FILE}" "${TMP_DIR}/${PPA_USER}-keyring.gpg"
echo -e "${COLOR_OK}Done${COLOR_OFF}"
else
echo -e "${COLOR_ERROR}ERROR: invalid input keyfile format${COLOR_OFF}"
rm -rf "${TMP_DIR}"
exit "${STATUS_ERROR}"
fi
# Move keyfile
echo -n "→ Moving keyfile to ${KEY_DIR}... "
# Check if keyfile exist
if [ -f "${TMP_DIR}/${PPA_USER}-keyring.gpg" ]; then
mkdir -p "${KEY_DIR}"
mv "${TMP_DIR}/${PPA_USER}-keyring.gpg" "${KEY_DIR}/${PPA_USER}-keyring.gpg"
echo -e "${COLOR_OK}Done${COLOR_OFF}"
else
echo -e "${COLOR_ERROR}ERROR: keyfile does not exist${COLOR_OFF}"
rm -rf "${TMP_DIR}"
exit "${STATUS_ERROR}"
fi
# Cleanup
echo -n "→ Cleaning temporary files... "
rm -rf "${TMP_DIR}"
echo -e "${COLOR_OK}Done${COLOR_OFF}"
# Exit
exit "${STATUS_OK}"
该脚本执行以下操作:
- 检查脚本是否被调用
sudo
- 检查是否
curl
已安装 - 必须将源文件
add-launchpad-ppa.conf
放在同一文件夹add-launchpad-ppa.sh
中 - 获取输入以构建一些内部变量。仅举一个例子,添加 musicbrainz picard 应用程序的输入(https://launchpad.net/~musicbrainz-developers/+archive/ubuntu/stable) 是
ppa:musicbrainz-developers/stable
(如“将此 PPA 添加到您的系统”段落中所述),因此添加此 ppa 的命令是sudo bash ~/.local/bin/add-launchpad-ppa.sh ppa:musicbrainz-developers/stable
(考虑到脚本位于~/.local/bin/
)。 - 根据以上数据,
.list
在 中创建文件/etc/apt/sources.list.d
。在本例中,文件将是/etc/apt/sources.list.d/musicbrainz-developers.list
- 解析 ppa 的 html 页面并下载(在临时文件夹中)ppa 密钥环。
- 使用命令将密钥转换为正确的格式
gpg
。这些转换命令(在if-elif-else
语句中)是从 AskUbuntu 中关于apt-key
弃用相关主题的几个答案中复制/修改的。 - 将密钥移动到
.conf
文件中配置的文件夹中。在示例中,密钥将是/etc/apt/keyrings/musicbrainz-developers-keyring.gpg
- 最后清理临时文件。
换句话说,它是本机命令的简单替换add-apt-repository
。此脚本还有很大的改进空间,我知道这一点,但就我个人而言,我发现它非常有用。