lxc-download: Rely on HTTPS only

GPG has been a major source of issues over the years with various
attacks on the key network as well as client side issues making it hard
to retrieve our keys.

Back when we introduced the image server, SSL certificates were still
expensive and annoying to setup, so not something we'd have expected
potential mirrors to setup for us. They were also issued for multiple
years, making a compromise of such a certificate quite problematic.

But things have changed since, we now have completely free, very easily
deployable SSL certificates everywhere with the majority of those being
shortlived and with good reporting of issued certificates.

With that, we can now deprecate the GPG validation, disable the fallback
to non-HTTPS download and rely on our indices being accurate because
they've been downloaded from a server with a valid certificate.

This puts LXC more in line with what LXD has done since the beginning
and should offer a more reliable user experience.

Signed-off-by: Stéphane Graber <stgraber@ubuntu.com>
This commit is contained in:
Stéphane Graber 2022-01-17 21:15:53 -05:00
parent 5d8c30856e
commit 5852026304
No known key found for this signature in database
GPG Key ID: C638974D64792D67

View File

@ -33,21 +33,15 @@ DOWNLOAD_DIST=
DOWNLOAD_FLUSH_CACHE="false" DOWNLOAD_FLUSH_CACHE="false"
DOWNLOAD_FORCE_CACHE="false" DOWNLOAD_FORCE_CACHE="false"
DOWNLOAD_INTERACTIVE="false" DOWNLOAD_INTERACTIVE="false"
DOWNLOAD_KEYID="0xE7FB0CAEC8173D669066514CBAEFF88C22F6E216"
DOWNLOAD_LIST_IMAGES="false" DOWNLOAD_LIST_IMAGES="false"
DOWNLOAD_MODE="system" DOWNLOAD_MODE="system"
DOWNLOAD_READY_GPG="false"
DOWNLOAD_RELEASE= DOWNLOAD_RELEASE=
DOWNLOAD_SERVER="images.linuxcontainers.org" DOWNLOAD_SERVER="images.linuxcontainers.org"
DOWNLOAD_SHOW_GPG_WARNING="true"
DOWNLOAD_SHOW_HTTP_WARNING="true"
DOWNLOAD_TARGET="system" DOWNLOAD_TARGET="system"
DOWNLOAD_URL= DOWNLOAD_URL=
DOWNLOAD_USE_CACHE="false" DOWNLOAD_USE_CACHE="false"
DOWNLOAD_VALIDATE="true"
DOWNLOAD_VARIANT="default" DOWNLOAD_VARIANT="default"
DOWNLOAD_TEMP= DOWNLOAD_TEMP=
DOWNLOAD_STANDARD_RESOLVER="false"
LXC_MAPPED_GID= LXC_MAPPED_GID=
LXC_MAPPED_UID= LXC_MAPPED_UID=
@ -55,16 +49,6 @@ LXC_NAME=
LXC_PATH= LXC_PATH=
LXC_ROOTFS= LXC_ROOTFS=
if [ -z "${DOWNLOAD_KEYSERVER:-}" ]; then
DOWNLOAD_KEYSERVER="hkp://keyserver.ubuntu.com"
# Deal with GPG over http proxy
if [ -n "${http_proxy:-}" ]; then
DOWNLOAD_KEYSERVER="hkp://keyserver.ubuntu.com:80"
DOWNLOAD_GPG_PROXY="--keyserver-options http-proxy=\"${http_proxy}\""
fi
fi
# Make sure the usual locations are in PATH # Make sure the usual locations are in PATH
export PATH="$PATH:/usr/sbin:/usr/bin:/sbin:/bin" export PATH="$PATH:/usr/sbin:/usr/bin:/sbin:/bin"
@ -87,88 +71,15 @@ wget_wrapper() {
download_file() { download_file() {
if ! wget_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then if ! wget_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then
if ! wget_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -T 30 -q "http://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then if [ "$3" = "noexit" ]; then
if [ "$3" = "noexit" ]; then return 1
return 1
else
echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
exit 1
fi
elif [ "${DOWNLOAD_SHOW_HTTP_WARNING}" = "true" ]; then
DOWNLOAD_SHOW_HTTP_WARNING="false"
echo "WARNING: Failed to download the file over HTTPs" 1>&2
echo " The file was instead download over HTTP " 1>&2
echo "A server replay attack may be possible!" 1>&2
fi
fi
}
download_sig() {
if ! download_file "$1" "$2" noexit; then
if [ "${DOWNLOAD_VALIDATE}" = "true" ]; then
if [ "$3" = "normal" ]; then
echo "ERROR: Failed to download http://${DOWNLOAD_SERVER}/$1" 1>&2
exit 1
else
return 1
fi
else else
return 0 echo "ERROR: Failed to download https://${DOWNLOAD_SERVER}/$1" 1>&2
exit 1
fi fi
fi fi
} }
gpg_setup() {
if [ "${DOWNLOAD_VALIDATE}" = "false" ]; then
return
fi
if [ "${DOWNLOAD_READY_GPG}" = "true" ]; then
return
fi
echo "Setting up the GPG keyring"
mkdir -p "${DOWNLOAD_TEMP}/gpg"
chmod 700 "${DOWNLOAD_TEMP}/gpg"
if [ "${DOWNLOAD_STANDARD_RESOLVER}" = "true" ]; then
echo "standard-resolver" > "${DOWNLOAD_TEMP}/gpg/dirmngr.conf"
fi
export GNUPGHOME="${DOWNLOAD_TEMP}/gpg"
success=
for _ in $(seq 3); do
if gpg --keyserver "${DOWNLOAD_KEYSERVER}" ${DOWNLOAD_GPG_PROXY:-} \
--recv-keys "${DOWNLOAD_KEYID}" >/dev/null 2>&1; then
success=1
break
fi
done
if [ -z "${success}" ]; then
echo "ERROR: Unable to fetch GPG key from keyserver"
exit 1
fi
DOWNLOAD_READY_GPG="true"
}
gpg_validate() {
if [ "${DOWNLOAD_VALIDATE}" = "false" ]; then
if [ "${DOWNLOAD_SHOW_GPG_WARNING}" = "true" ]; then
echo "WARNING: Running without gpg validation!" 1>&2
fi
DOWNLOAD_SHOW_GPG_WARNING="false"
return 0
fi
if ! gpg --verify "$1" >/dev/null 2>&1; then
echo "ERROR: Invalid signature for $1" 1>&2
exit 1
fi
}
in_userns() { in_userns() {
[ -e /proc/self/uid_map ] || { echo no; return; } [ -e /proc/self/uid_map ] || { echo no; return; }
while read -r line; do while read -r line; do
@ -222,12 +133,8 @@ Required arguments:
Optional arguments: Optional arguments:
[ --variant <variant> ]: Variant of the image (default: "default") [ --variant <variant> ]: Variant of the image (default: "default")
[ --server <server> ]: Image server (default: "images.linuxcontainers.org") [ --server <server> ]: Image server (default: "images.linuxcontainers.org")
[ --keyid <keyid> ]: GPG keyid (default: 0x...)
[ --keyserver <keyserver> ]: GPG keyserver to use. Environment variable: DOWNLOAD_KEYSERVER
[ --no-validate ]: Disable GPG validation (not recommended)
[ --flush-cache ]: Flush the local copy (if present) [ --flush-cache ]: Flush the local copy (if present)
[ --force-cache ]: Force the use of the local copy even if expired [ --force-cache ]: Force the use of the local copy even if expired
[ --standard-resolver ]: Force the use of the standard resolver
LXC internal arguments (do not pass manually!): LXC internal arguments (do not pass manually!):
[ --name <name> ]: The container name [ --name <name> ]: The container name
@ -236,16 +143,12 @@ LXC internal arguments (do not pass manually!):
[ --mapped-uid <map> ]: A uid map (user namespaces) [ --mapped-uid <map> ]: A uid map (user namespaces)
[ --mapped-gid <map> ]: A gid map (user namespaces) [ --mapped-gid <map> ]: A gid map (user namespaces)
Environment Variables:
DOWNLOAD_KEYSERVER : The URL of the key server to use, instead of the default.
Can be further overridden by using optional argument --keyserver
EOF EOF
return 0 return 0
} }
if ! options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\ if ! options=$(getopt -o d:r:a:hl -l dist:,release:,arch:,help,list,variant:,\
server:,keyid:,keyserver:,no-validate,flush-cache,force-cache,name:,path:,\ server:,flush-cache,force-cache,name:,path:,\
rootfs:,mapped-uid:,mapped-gid: -- "$@"); then rootfs:,mapped-uid:,mapped-gid: -- "$@"); then
usage usage
exit 1 exit 1
@ -261,12 +164,8 @@ while :; do
-a|--arch) DOWNLOAD_ARCH="$2"; shift 2;; -a|--arch) DOWNLOAD_ARCH="$2"; shift 2;;
--variant) DOWNLOAD_VARIANT="$2"; shift 2;; --variant) DOWNLOAD_VARIANT="$2"; shift 2;;
--server) DOWNLOAD_SERVER="$2"; shift 2;; --server) DOWNLOAD_SERVER="$2"; shift 2;;
--keyid) DOWNLOAD_KEYID="$2"; shift 2;;
--keyserver) DOWNLOAD_KEYSERVER="$2"; shift 2;;
--no-validate) DOWNLOAD_VALIDATE="false"; shift 1;;
--flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;; --flush-cache) DOWNLOAD_FLUSH_CACHE="true"; shift 1;;
--force-cache) DOWNLOAD_FORCE_CACHE="true"; shift 1;; --force-cache) DOWNLOAD_FORCE_CACHE="true"; shift 1;;
--standard-resolver) STANDARD_RESOLVER="true"; shift 1;;
--name) LXC_NAME="$2"; shift 2;; --name) LXC_NAME="$2"; shift 2;;
--path) LXC_PATH="$2"; shift 2;; --path) LXC_PATH="$2"; shift 2;;
--rootfs) LXC_ROOTFS="$2"; shift 2;; --rootfs) LXC_ROOTFS="$2"; shift 2;;
@ -284,15 +183,6 @@ for bin in tar xz wget; do
fi fi
done done
# Check for GPG
if [ "${DOWNLOAD_VALIDATE}" = "true" ]; then
if ! command -V gpg >/dev/null 2>&1; then
echo "ERROR: Missing recommended tool: gpg" 1>&2
echo "You can workaround this by using --no-validate" 1>&2
exit 1
fi
fi
# Check that we have all variables we need # Check that we have all variables we need
if [ -z "${LXC_NAME}" ] || [ -z "${LXC_PATH}" ] || [ -z "${LXC_ROOTFS}" ]; then if [ -z "${LXC_NAME}" ] || [ -z "${LXC_PATH}" ] || [ -z "${LXC_ROOTFS}" ]; then
if [ "${DOWNLOAD_LIST_IMAGES}" != "true" ]; then if [ "${DOWNLOAD_LIST_IMAGES}" != "true" ]; then
@ -340,21 +230,14 @@ fi
# Simply list images # Simply list images
if [ "${DOWNLOAD_LIST_IMAGES}" = "true" ] || [ "${DOWNLOAD_INTERACTIVE}" = "true" ]; then if [ "${DOWNLOAD_LIST_IMAGES}" = "true" ] || [ "${DOWNLOAD_INTERACTIVE}" = "true" ]; then
# Initialize GPG
gpg_setup
# Grab the index # Grab the index
DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}" DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}"
echo "Downloading the image index" echo "Downloading the image index"
if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" "${DOWNLOAD_TEMP}/index" noexit || if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" "${DOWNLOAD_TEMP}/index" noexit; then
! download_sig "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc" "${DOWNLOAD_TEMP}/index.asc" noexit; then
download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal
download_sig "${DOWNLOAD_INDEX_PATH}.asc" "${DOWNLOAD_TEMP}/index.asc" normal
fi fi
gpg_validate "${DOWNLOAD_TEMP}/index.asc"
# Parse it # Parse it
echo "" echo ""
echo "---" echo "---"
@ -429,21 +312,14 @@ fi
# Download what's needed # Download what's needed
if [ "${DOWNLOAD_USE_CACHE}" = "false" ]; then if [ "${DOWNLOAD_USE_CACHE}" = "false" ]; then
# Initialize GPG
gpg_setup
# Grab the index # Grab the index
DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}" DOWNLOAD_INDEX_PATH="/meta/1.0/index-${DOWNLOAD_MODE}"
echo "Downloading the image index" echo "Downloading the image index"
if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" "${DOWNLOAD_TEMP}/index" noexit || if ! download_file "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}" "${DOWNLOAD_TEMP}/index" noexit; then
! download_sig "${DOWNLOAD_INDEX_PATH}.${DOWNLOAD_COMPAT_LEVEL}.asc" "${DOWNLOAD_TEMP}/index.asc" noexit; then
download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal download_file "${DOWNLOAD_INDEX_PATH}" "${DOWNLOAD_TEMP}/index" normal
download_sig "${DOWNLOAD_INDEX_PATH}.asc" "${DOWNLOAD_TEMP}/index.asc" normal
fi fi
gpg_validate "${DOWNLOAD_TEMP}/index.asc"
# Parse it # Parse it
while IFS=';' read -r f1 f2 f3 f4 f5 f6; do while IFS=';' read -r f1 f2 f3 f4 f5 f6; do
if [ "${f1}" != "${DOWNLOAD_DIST}" ] || \ if [ "${f1}" != "${DOWNLOAD_DIST}" ] || \
@ -474,13 +350,9 @@ if [ "${DOWNLOAD_USE_CACHE}" = "false" ]; then
# Download the actual files # Download the actual files
echo "Downloading the rootfs" echo "Downloading the rootfs"
download_file "${DOWNLOAD_URL}/rootfs.tar.xz" "${DOWNLOAD_TEMP}/rootfs.tar.xz" normal download_file "${DOWNLOAD_URL}/rootfs.tar.xz" "${DOWNLOAD_TEMP}/rootfs.tar.xz" normal
download_sig "${DOWNLOAD_URL}/rootfs.tar.xz.asc" "${DOWNLOAD_TEMP}/rootfs.tar.xz.asc" normal
gpg_validate "${DOWNLOAD_TEMP}/rootfs.tar.xz.asc"
echo "Downloading the metadata" echo "Downloading the metadata"
download_file "${DOWNLOAD_URL}/meta.tar.xz" "${DOWNLOAD_TEMP}/meta.tar.xz" normal download_file "${DOWNLOAD_URL}/meta.tar.xz" "${DOWNLOAD_TEMP}/meta.tar.xz" normal
download_sig "$DOWNLOAD_URL/meta.tar.xz.asc" "${DOWNLOAD_TEMP}/meta.tar.xz.asc" normal
gpg_validate "${DOWNLOAD_TEMP}/meta.tar.xz.asc"
if [ -d "${LXC_CACHE_PATH}" ]; then if [ -d "${LXC_CACHE_PATH}" ]; then
rm -Rf "${LXC_CACHE_PATH}" rm -Rf "${LXC_CACHE_PATH}"