Merge pull request #3162 from flatcar/danzatt/sign-sysexts

Signed OS-dependent sysexts
This commit is contained in:
Daniel 2025-12-01 11:12:22 +01:00 committed by GitHub
commit 0945652715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 403 additions and 14 deletions

View File

@ -293,6 +293,7 @@ dev-libs/tree-sitter
dev-libs/tree-sitter-bash
dev-libs/userspace-rcu
dev-libs/xmlsec
dev-libs/xxhash
dev-libs/yajl
dev-perl/File-Slurper
@ -671,6 +672,7 @@ sys-fs/btrfs-progs
sys-fs/cryptsetup
sys-fs/dosfstools
sys-fs/e2fsprogs
sys-fs/erofs-utils
sys-fs/fuse
sys-fs/fuse-common
sys-fs/fuse-overlayfs
@ -750,6 +752,7 @@ virtual/service-manager
virtual/ssh
virtual/tmpfiles
virtual/udev
virtual/zlib
x11-drivers/nvidia-drivers

View File

@ -170,6 +170,10 @@ EOF
# Remove source locale data, only need to ship the compiled archive.
sudo rm -rf ${root_fs_dir}/usr/share/i18n/
# Inject ephemeral sysext signing certificate
sudo mkdir -p "${root_fs_dir}/usr/lib/verity.d"
sudo cp "${SYSEXT_SIGNING_KEY_DIR}/sysexts.crt" "${root_fs_dir}/usr/lib/verity.d"
# Finish image will move files from /etc to /usr/share/flatcar/etc.
# Note that image filesystem contents generated by finish_image will not
# include sysext contents (only the sysext squashfs files themselves).

View File

@ -63,7 +63,7 @@ create_prod_sysext() {
# The --install_root_basename="${name}-base-sysext-rootfs" flag is
# important - it sets the name of a rootfs directory, which is used
# to determine the package target in coreos/base/profile.bashrc
sudo "FLATCAR_BUILD_ID=$FLATCAR_BUILD_ID" "${SCRIPTS_DIR}/build_sysext" \
sudo -E "FLATCAR_BUILD_ID=$FLATCAR_BUILD_ID" "${SCRIPTS_DIR}/build_sysext" \
--board="${BOARD}" \
--image_builddir="${workdir}/sysext-build" \
--squashfs_base="${base_sysext}" \
@ -99,6 +99,14 @@ sysext_mountdir="${BUILD_DIR}/prod-sysext-work/mounts"
sysext_base="${sysext_workdir}/base-os.squashfs"
function cleanup() {
IFS=':' read -r -a mounted_sysexts <<< "$sysext_lowerdirs"
# skip the rootfs
mounted_sysexts=("${mounted_sysexts[@]:1}")
for sysext in "${mounted_sysexts[@]}"; do
sudo systemd-dissect --umount --rmdir "$sysext"
done
sudo umount "${sysext_mountdir}"/* || true
rm -rf "${sysext_workdir}" || true
}
@ -116,6 +124,7 @@ sudo mksquashfs "${root_fs_dir}" "${sysext_base}" -noappend -xattrs-exclude '^bt
# for combined overlay later.
prev_pkginfo=""
sysext_lowerdirs="${sysext_mountdir}/rootfs-lower"
mkdir -p "${sysext_mountdir}"
for sysext in ${sysexts_list//,/ }; do
# format is "<name>:<group>/<package>"
name="${sysext%|*}"
@ -129,11 +138,20 @@ for sysext in ${sysexts_list//,/ }; do
"${grp_pkg}" \
"${prev_pkginfo}"
mkdir -p "${sysext_mountdir}/${name}" \
"${sysext_mountdir}/${name}_pkginfo"
sudo mount -rt squashfs -o loop,nodev "${sysext_output_dir}/${name}.raw" \
sudo systemd-dissect \
--read-only \
--mount \
--mkdir \
--image-policy='root=encrypted+unprotected+absent:usr=encrypted+unprotected+absent' \
"${sysext_output_dir}/${name}.raw" \
"${sysext_mountdir}/${name}"
sudo mount -rt squashfs -o loop,nodev "${sysext_output_dir}/${name}_pkginfo.raw" \
sudo systemd-dissect \
--read-only \
--mount \
--mkdir \
--image-policy='root=encrypted+unprotected+absent:usr=encrypted+unprotected+absent' \
"${sysext_output_dir}/${name}_pkginfo.raw" \
"${sysext_mountdir}/${name}_pkginfo"
sysext_lowerdirs="${sysext_lowerdirs}:${sysext_mountdir}/${name}"

View File

@ -602,7 +602,7 @@ install_oem_sysext() {
fi
mkdir -p "${built_sysext_dir}"
sudo "${build_sysext_env[@]}" "${SCRIPT_ROOT}/build_sysext" "${build_sysext_flags[@]}" "${oem_sysext}"
sudo -E "${build_sysext_env[@]}" "${SCRIPT_ROOT}/build_sysext" "${build_sysext_flags[@]}" "${oem_sysext}"
local installed_sysext_oem_dir='/oem/sysext'
local installed_sysext_file_prefix="${oem_sysext}-${version}"

View File

@ -304,14 +304,25 @@ if [[ -n "${invalid_files}" ]]; then
die "Invalid file ownership: ${invalid_files}"
fi
mksquashfs "${BUILD_DIR}/${FLAGS_install_root_basename}" "${BUILD_DIR}/${SYSEXTNAME}.raw" \
-noappend -xattrs-exclude '^btrfs.' -comp "${FLAGS_compression}" ${FLAGS_mksquashfs_opts}
systemd-repart \
--private-key="${SYSEXT_SIGNING_KEY_DIR}/sysexts.key" \
--certificate="${SYSEXT_SIGNING_KEY_DIR}/sysexts.crt" \
--make-ddi=sysext \
--copy-source="${BUILD_DIR}/${FLAGS_install_root_basename}" \
"${BUILD_DIR}/${SYSEXTNAME}.raw"
rm -rf "${BUILD_DIR}"/{fs-root,"${FLAGS_install_root_basename}",workdir}
# Generate reports
mkdir "${BUILD_DIR}/img-rootfs"
mount -rt squashfs -o loop,nodev "${BUILD_DIR}/${SYSEXTNAME}.raw" "${BUILD_DIR}/img-rootfs"
systemd-dissect --read-only \
--mount \
--mkdir \
--image-policy='root=encrypted+unprotected+absent:usr=encrypted+unprotected+absent' \
"${BUILD_DIR}/${SYSEXTNAME}.raw" \
"${BUILD_DIR}/img-rootfs"
write_contents "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_contents.txt"
write_contents_with_technical_details "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_contents_wtd.txt"
write_disk_space_usage_in_paths "${BUILD_DIR}/img-rootfs" "${BUILD_DIR}/${SYSEXTNAME}_disk_usage.txt"
umount "${BUILD_DIR}/img-rootfs"
systemd-dissect --umount --rmdir "${BUILD_DIR}/img-rootfs"

View File

@ -0,0 +1 @@
- OS-dependent sysexts (e.g., docker-flatcar, containerd-flatcar) are now cryptographically signed using dm-verity roothash signatures. This enables stricter sysext policies via systemd-sysext and provides a foundation for verifying user-provided extensions in future releases. The format changed from squashfs to erofs-based Discoverable Disk Images (DDI). ([scripts#3162](https://github.com/flatcar/scripts/pull/3162))

View File

@ -1 +1 @@
DIST azure-keyvault-pkcs11-0_p20250526.tar.gz 22829 BLAKE2B 59df337d32c1931577cd6538a53032fc9f5a43ce67192d114b23b84adfb36c234e091c9cbf2183efc080d093a6c17b7596abd3e7789ffbbd0634912d16d92693 SHA512 410f3f4e446aa1c1307769bc021f39ec9dd01cd08c2a3089889ad382f2b1948bd03eb065970901982a014a31f4bef4cd102a14a39286a7518736b59b4d0ee03f
DIST azure-keyvault-pkcs11-0_p20250905.tar.gz 22855 BLAKE2B e380d091ef486b988cc3720ae16f00082af69eb8f2dab4f1ee9729e3f18ea3ec06c39cf774aed6a887fba14190431592e7bfc5cb161f3b1a2cc82a050a1d4758 SHA512 902ec4a31e52f3d480dac485c12569813c108fed69b968b42a0262b3d94bcbe6b79ac54c801dec3f44141dcb387d04873ddccd99bf06ed46c93bc2fb919374f7

View File

@ -5,7 +5,7 @@ EAPI=8
inherit cmake
COMMIT="126ae2bc714f2867b6628b49962f388c4b314f5f"
COMMIT="c72d89bf0b17f8c21a93870efaaabb93c0dc9c63"
DESCRIPTION="PKCS#11 module for Azure Key Vault"
HOMEPAGE="https://github.com/jepio/azure_keyvault_pkcs11"
SRC_URI="https://github.com/jepio/azure_keyvault_pkcs11/archive/${COMMIT}.tar.gz -> ${P}.tar.gz"

View File

@ -46,6 +46,7 @@ DEPEND="
sys-firmware/edk2-bin
sys-fs/btrfs-progs
sys-fs/cryptsetup
sys-fs/erofs-utils
dev-perl/Parse-Yapp
dev-util/pkgcheck
"

View File

@ -1 +1,5 @@
# Temporarily put the SDK version ahead for sd-json support in Dracut.
# Needed for building signed sysexts with systemd-repart
dev-libs/xxhash
sys-fs/erofs-utils

View File

@ -31,3 +31,6 @@ x11-libs/pixman static-libs
# Get latest EDK2 firmware for Secure Boot on arm64.
app-emulation/qemu -pin-upstream-blobs
# Needed for signed sysexts using systemd-repart
sys-apps/systemd cryptsetup

View File

@ -0,0 +1,2 @@
DIST xxhash-0.8.2.tar.gz 1141188 BLAKE2B 735408256240760778fa516e01bed428f04837eb4e059c512e924f13e4a96db6cacbbefb04dea65a37b0f25b52cf13c4927a6e7870dc8c0d45b1b955d4ba3da1 SHA512 3e3eef21432fe88bc4dd9940ccad0308fdea3537b06fa5ac0e74c1bde53413dff29c8b3fc617a8a42b9ce88fcf213311d338a31b1ce73b3729342c9e68f06c78
DIST xxhash-0.8.3.tar.gz 1147630 BLAKE2B 75923c7c5df3490062791fa02ccddfb7281b3646e2b3e4b4a0c0d611c339e07c8d9cb656777fd0fcec9cda484f7b33edf080116bb011f70d6b8299cda63afa4e SHA512 8b5c8b9aad4e869f28310b12cc314037feda81d92f26c23eaecdb35dc65042ca2e65f2e9606033e62a31bcc737a9a950500ffcbdb8677d6ab20e820ea14f2b79

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
<maintainer type="person">
<email>amadio@gentoo.org</email>
<name>Guilherme Amadio</name>
</maintainer>
<upstream>
<remote-id type="github">Cyan4973/xxHash</remote-id>
<bugs-to>https://github.com/Cyan4973/xxHash/issues</bugs-to>
</upstream>
</pkgmetadata>

View File

@ -0,0 +1,43 @@
# Copyright 1999-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit multilib-minimal toolchain-funcs
DESCRIPTION="Extremely fast non-cryptographic hash algorithm"
HOMEPAGE="https://xxhash.com/"
SRC_URI="https://github.com/Cyan4973/xxHash/archive/v${PV}.tar.gz -> ${P}.tar.gz"
S=${WORKDIR}/xxHash-${PV}
LICENSE="BSD-2 GPL-2+"
# https://abi-laboratory.pro/tracker/timeline/xxhash
SLOT="0"
KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~x64-macos"
src_prepare() {
default
multilib_copy_sources
}
multilib_src_compile() {
emake AR="$(tc-getAR)" CC="$(tc-getCC)"
}
multilib_src_test() {
emake CC="$(tc-getCC)" check
}
multilib_src_install() {
local emakeargs=(
DESTDIR="${D}"
PREFIX="${EPREFIX}"/usr
LIBDIR="${EPREFIX}"/usr/$(get_libdir)
)
emake "${emakeargs[@]}" install
einstalldocs
rm "${ED}"/usr/$(get_libdir)/libxxhash.a || die
}

View File

@ -0,0 +1,57 @@
# Copyright 1999-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit flag-o-matic multilib-minimal toolchain-funcs
DESCRIPTION="Extremely fast non-cryptographic hash algorithm"
HOMEPAGE="https://xxhash.com/"
SRC_URI="https://github.com/Cyan4973/xxHash/archive/v${PV}.tar.gz -> ${P}.tar.gz"
S=${WORKDIR}/xxHash-${PV}
LICENSE="BSD-2 GPL-2+"
SLOT="0"
KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~x64-macos"
src_prepare() {
default
multilib_copy_sources
}
src_configure() {
# Needed for -Og to be buildable, otherwise fails a/ always_inline (bug #961093)
# https://github.com/Cyan4973/xxHash?tab=readme-ov-file#binary-size-control
is-flagq '-Og' && append-cppflags -DXXH_NO_INLINE_HINTS
multilib-minimal_src_configure
}
myemake() {
emake \
AR="$(tc-getAR)" \
CC="$(tc-getCC)" \
"${@}"
}
multilib_src_compile() {
myemake
}
multilib_src_test() {
# Injecting CPPFLAGS into CFLAGS is needed for test_sanity
myemake CFLAGS="${CPPFLAGS} ${CFLAGS}" check
}
multilib_src_install() {
local emakeargs=(
DESTDIR="${D}"
PREFIX="${EPREFIX}"/usr
LIBDIR="${EPREFIX}"/usr/$(get_libdir)
)
myemake "${emakeargs[@]}" install
einstalldocs
rm "${ED}"/usr/$(get_libdir)/libxxhash.a || die
}

View File

@ -0,0 +1,2 @@
DIST erofs-utils-1.8.10.tar.gz 201240 BLAKE2B 9575258115192dc0ef7999dd1fa44f619f9f62c93cfe28e3e65fd5d9a601b09acdce293552c90f51c2f7b557d8830e856d6706af6ccd98fc157b05f819d136b4 SHA512 8c7afd3db55fd4c4f7aa9fbd7ed40fa40de0bfffcee601a3f5dce823d406a32b5e939e24cd6dc336e3033e940fb16ee93f8821f627f90b10e6137113949933dd
DIST erofs-utils-1.8.4.tar.gz 187276 BLAKE2B da0d80abbfd9b2d547c30bad7647165a3500f20e5de0b5db4c54efb27ec895fd069be983193b06d35728f5a8e1490e6cd255207c76135d8978d86e1512430755 SHA512 c941b0a2ab6c650a9aa4c9cadeb277ebc87007dc51354ff013c7cb763e6e8c9d44ed9e4791730ed05088faaba8c612198b924e70f5e52019382cfdf6d2e6b677

View File

@ -0,0 +1,63 @@
# Copyright 2021-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit autotools
DESCRIPTION="Userspace tools for EROFS"
HOMEPAGE="https://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git"
SRC_URI="https://git.kernel.org/pub/scm/linux/kernel/git/xiang/${PN}.git/snapshot/${P}.tar.gz"
LICENSE="GPL-2+"
SLOT="0"
KEYWORDS="~amd64 ~arm64 ~loong"
IUSE="fuse libdeflate +lz4 +lzma selinux static-libs +threads +uuid +zlib +zstd"
RDEPEND="
dev-libs/xxhash:0=
fuse? ( sys-fs/fuse:0 )
lz4? ( app-arch/lz4:0= )
lzma? ( >=app-arch/xz-utils-5.4.0:0= )
selinux? ( sys-libs/libselinux:0= )
uuid? ( sys-apps/util-linux )
zlib? (
libdeflate? ( app-arch/libdeflate:0= )
!libdeflate? ( virtual/zlib:= )
)
zstd? ( app-arch/zstd:0= )
"
DEPEND="${RDEPEND}"
BDEPEND="virtual/pkgconfig"
PATCHES=(
)
src_prepare() {
default
eautoreconf
}
src_configure() {
local myeconfargs=(
--disable-werror
$(use_enable fuse)
$(use_with libdeflate)
$(use_enable lz4)
$(use_enable lzma)
$(use_with selinux)
$(use_enable static-libs static-fuse)
$(use_enable threads multithreading)
$(use_with uuid)
$(use_with zlib)
$(use_with zstd libzstd)
--without-qpl # not packaged
# do not use bundled xxhash; also upstream says "expected to be
# faster than the internal one"
--with-xxhash
)
econf "${myeconfargs[@]}"
}

View File

@ -0,0 +1,63 @@
# Copyright 2021-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit autotools
DESCRIPTION="Userspace tools for EROFS"
HOMEPAGE="https://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git"
SRC_URI="https://git.kernel.org/pub/scm/linux/kernel/git/xiang/${PN}.git/snapshot/${P}.tar.gz"
LICENSE="GPL-2+"
SLOT="0"
KEYWORDS="~amd64 ~arm64 ~loong"
IUSE="fuse libdeflate +lz4 +lzma selinux static-libs +threads +uuid +zlib +zstd"
RDEPEND="
dev-libs/xxhash:0=
fuse? ( sys-fs/fuse:0 )
lz4? ( app-arch/lz4:0= )
lzma? ( >=app-arch/xz-utils-5.4.0:0= )
selinux? ( sys-libs/libselinux:0= )
uuid? ( sys-apps/util-linux )
zlib? (
libdeflate? ( app-arch/libdeflate:0= )
!libdeflate? ( virtual/zlib:= )
)
zstd? ( app-arch/zstd:0= )
"
DEPEND="${RDEPEND}"
BDEPEND="virtual/pkgconfig"
PATCHES=(
)
src_prepare() {
default
eautoreconf
}
src_configure() {
local myeconfargs=(
--disable-werror
$(use_enable fuse)
$(use_with libdeflate)
$(use_enable lz4)
$(use_enable lzma)
$(use_with selinux)
$(use_enable static-libs static-fuse)
$(use_enable threads multithreading)
$(use_with uuid)
$(use_with zlib)
$(use_with zstd libzstd)
--without-qpl # not packaged
# do not use bundled xxhash; also upstream says "expected to be
# faster than the internal one"
--with-xxhash
)
econf "${myeconfargs[@]}"
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
<maintainer type="person">
<email>xen0n@gentoo.org</email>
<name>WANG Xuerui</name>
</maintainer>
<use>
<flag name="fuse">Builds erofsfuse (requires <pkg>sys-fs/fuse</pkg>).</flag>
<flag name="libdeflate">Use <pkg>app-arch/libdeflate</pkg> rather than <pkg>virtual/zlib</pkg> for handling deflate compression.</flag>
<flag name="uuid">Enables UUID support via <pkg>sys-apps/util-linux</pkg>.</flag>
</use>
</pkgmetadata>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
<maintainer type="project">
<email>base-system@gentoo.org</email>
<name>Gentoo Base System</name>
</maintainer>
<use>
<flag name="minizip">include the minizip library for quick and dirty zip extraction</flag>
</use>
</pkgmetadata>

View File

@ -0,0 +1,18 @@
# Copyright 2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit multilib-build
DESCRIPTION="Virtual for libz.so providers"
SLOT="0/1"
KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~m68k ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~amd64-linux ~x86-linux ~arm64-macos ~ppc-macos ~x64-macos ~x64-solaris"
IUSE="static-libs"
RDEPEND="
|| (
>=sys-libs/zlib-1.3.1[${MULTILIB_USEDEP},static-libs?]
sys-libs/zlib-ng[${MULTILIB_USEDEP},compat,static-libs(-)?]
)
"

View File

@ -0,0 +1,23 @@
# Copyright 2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit multilib-build
DESCRIPTION="Virtual for libz.so providers"
SLOT="0/1"
KEYWORDS="~alpha amd64 arm arm64 ~hppa ~loong ~m68k ~mips ppc ppc64 ~riscv ~s390 ~sparc x86 ~amd64-linux ~x86-linux ~arm64-macos ~ppc-macos ~x64-macos ~x64-solaris"
IUSE="minizip static-libs"
RDEPEND="
|| (
>=sys-libs/zlib-1.3.1[${MULTILIB_USEDEP},minizip?,static-libs?]
(
sys-libs/zlib-ng[${MULTILIB_USEDEP},compat,static-libs(-)?]
minizip? (
sys-libs/minizip-ng[${MULTILIB_USEDEP},compat,static-libs(-)?]
)
)
)
"

View File

@ -88,6 +88,43 @@ if ! grep -q 'export MODULE_SIGNING_KEY_DIR=' /home/sdk/.bashrc; then
fi
fi
# Ensure sysext signing keys exist; regenerate if directory or files missing
if grep -q 'export SYSEXT_SIGNING_KEY_DIR' /home/sdk/.bashrc; then
_existing_sysext_dir=$(source /home/sdk/.bashrc 2>/dev/null; echo "$SYSEXT_SIGNING_KEY_DIR")
if [[ -z "$_existing_sysext_dir" || ! -d "$_existing_sysext_dir" || ! -s "$_existing_sysext_dir/sysexts.key" || ! -s "$_existing_sysext_dir/sysexts.crt" ]]; then
# Drop stale export so block below regenerates
sed -i -e '/export SYSEXT_SIGNING_KEY_DIR=/d' /home/sdk/.bashrc
fi
fi
grep -q 'export SYSEXT_SIGNING_KEY_DIR' /home/sdk/.bashrc || {
if [[ ${COREOS_OFFICIAL:-0} -eq 1 ]]; then
SYSEXT_SIGNING_KEY_DIR=$(su sdk -c "mktemp -d")
else
SYSEXT_SIGNING_KEY_DIR="/home/sdk/.sysext-signing-keys"
su sdk -c "mkdir -p ${SYSEXT_SIGNING_KEY_DIR@Q}"
fi
if [[ ! "$SYSEXT_SIGNING_KEY_DIR" || ! -d "$SYSEXT_SIGNING_KEY_DIR" ]]; then
echo "Failed to create directory for sysext signing keys."
else
echo "export SYSEXT_SIGNING_KEY_DIR='$SYSEXT_SIGNING_KEY_DIR'" >> /home/sdk/.bashrc
fi
pushd "$SYSEXT_SIGNING_KEY_DIR" > /dev/null
build_id=$(source "/mnt/host/source/.repo/manifests/version.txt"; echo "$FLATCAR_BUILD_ID")
# Generate sysext signing key only if missing or empty
if [[ ! -s sysexts.key || ! -s sysexts.crt ]]; then
su sdk -c "openssl req -new -nodes -utf8 \
-x509 -batch -sha256 \
-days 36000 \
-outform PEM \
-out sysexts.crt \
-keyout sysexts.key \
-newkey 4096 \
-subj '/CN=Flatcar sysext key/OU=$build_id'" \
|| echo "Generating sysext signing key failed"
fi
popd > /dev/null
}
# This is ugly.
# We need to sudo su - sdk -c so the SDK user gets a fresh login.
# 'sdk' is member of multiple groups, and plain docker USER only