diff options
-rw-r--r-- | classes/swupd-client.bbclass | 109 | ||||
-rw-r--r-- | classes/swupd-image.bbclass | 147 | ||||
-rw-r--r-- | conf/swupd/swupd-config.inc | 55 |
3 files changed, 167 insertions, 144 deletions
diff --git a/classes/swupd-client.bbclass b/classes/swupd-client.bbclass new file mode 100644 index 0000000..f380eb3 --- /dev/null +++ b/classes/swupd-client.bbclass @@ -0,0 +1,109 @@ +# Class for swupd client integration -- adds necessary bits to +# rootfs images that contain the swupd-client, i.e. setting the +# expected format version, URLs, and public keys. +# +# Usage: +# * inherit this class if you wish to create an image that contains the +# swupd-client, but where the image itself is not subject to any +# swupd based processing +# +# An example would be an initramfs that contains the client, but that +# initramfs itself is a regular file in a different (outer) file system +# (image). The outer image would be subject to swupd processing, and the +# inner initramfs is simply responsible to update the outer file system +# during system (re)start. + +require conf/swupd/swupd-config.inc + +PACKAGE_INSTALL_append = " swupd-client-format${SWUPD_TOOLS_FORMAT}" + +# The information about where to find version information and actual +# content is needed in several places: +# - the swupd client in the image gets configured such that it uses that as default +# - swupd server needs information about the previous build +# +# The version URL determines what the client picks as the version that it updates to. +# The content URL must have all builds ever produced and is expected to also +# have the corresponding version information. +# +# To build the very first version of an image, set these to empty. +# Errors while accessing the server (as the non-existent download.example.com) +# or not having any previous build on that server are fatal. The latter +# is necessary to detect misconfiguration. +SWUPD_VERSION_URL ??= "http://download.example.com/updates/my-distro/milestone/${MACHINE}/${SWUPD_IMAGE_PN}" +SWUPD_CONTENT_URL ??= "http://download.example.com/updates/my-distro/builds/${MACHINE}/${SWUPD_IMAGE_PN}" + +# An absolute path for a file containing the SSL certificate that is +# is to be used for verifying https connections to the version and content +# derver. +SWUPD_PINNED_PUBKEY ??= "" + + +# swupd-client checks VERSION_ID, which must match the OS_VERSION +# used for generating swupd bundles in the current build. +# +# We patch this during image creation and exclude OS_VERSION from the +# dependencies because doing it during the compilation of os-release.bb +# would trigger a rebuild even if all that changed is the OS_VERSION. +# It would also affect builds of images where swupd is not active. Both +# is undesirable. +# +# If triggering a rebuild on each OS_VERSION change is desired, +# then this can be achieved by influencing the os-release package +# by setting in local.conf: +# VERSION_ID = "${OS_VERSION}" +PACKAGE_INSTALL_append = " os-release" +swupd_patch_os_release () { + sed -i -e 's/^VERSION_ID *=.*/VERSION_ID="${OS_VERSION}"/' ${IMAGE_ROOTFS}/usr/lib/os-release +} +swupd_patch_os_release[vardepsexclude] = "OS_VERSION" +ROOTFS_POSTPROCESS_COMMAND += "swupd_patch_os_release; " + + +def hash_swupd_pinned_pubkey(d): + pubkey = d.getVar('SWUPD_PINNED_PUBKEY', True) + if pubkey: + import hashlib + bb.parse.mark_dependency(d, pubkey) + with open(pubkey, 'rb') as f: + hash = hashlib.sha256() + hash.update(f.read()) + return hash.hexdigest() + else: + return '' + +SWUPD_PINNED_PUBKEY_HASH := "${@ hash_swupd_pinned_pubkey(d)}" + +# The swupd client must be configured on a per-image basis. +# Different images might need different settings. +configure_swupd_client () { + # Write default values to the configuration hierarchy (since 3.4.0) + install -d ${IMAGE_ROOTFS}/usr/share/defaults/swupd + echo "${SWUPD_VERSION_URL}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/versionurl + echo "${SWUPD_CONTENT_URL}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/contenturl + echo "${SWUPD_FORMAT}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/format + # Changing content of the pubkey also changes the hash and thus ensures + # that this method and thus do_rootfs run again. + # + # TODO: does not actually work. Recipe gets reparsed when the file + # changes ("bitbake -e ostro-image-swupd | SWUPD_PINNED_PUBKEY_HASH" changes) + # but the task does not get re-executed. Forcing that leads to: + # + # ERROR: ostro-image-swupd-1.0-r0 do_rootfs: Taskhash mismatch 8762bf20b997ac29dd6793fd11e609c3 versus cb40afac8ca291e31022d5ffd9a9bbac for /work/ostro-os/meta-ostro/recipes-image/images/ostro-image-swupd.bb.do_rootfs + # ERROR: Taskhash mismatch 8762bf20b997ac29dd6793fd11e609c3 versus cb40afac8ca291e31022d5ffd9a9bbac for /work/ostro-os/meta-ostro/recipes-image/images/ostro-image-swupd.bb.do_rootfs + # + # $ bitbake-diffsigs tmp-glibc/stamps/qemux86-ostro-linux/ostro-image-swupd/1.0-r0.do_rootfs.sigdata.c8a9371831f58ce4f8b49a73211f66aa tmp-glibc/stamps/qemux86-ostro-linux/ostro-image-swupd/1.0-r0.do_rootfs.sigdata.cb40afac8ca291e31022d5ffd9a9bbac + # basehash changed from 02de100ee7baa348e224f21844fdaa06 to e3bb23a069673a09afee4994522991d3 + # Variable SWUPD_PINNED_PUBKEY_HASH value changed from 'b9ffbe0963f3f7ab3f3c1af5cd8471c121cb601eb4294ad4b211f1e206746a0a' to '8d172423eb0162feb8c7fb2f2d7da28a6effdf3e95184114c62e6b0efdeae89a' + # Taint (by forced/invalidated task) changed from None to 2c8e3b43-5e70-4c96-bf6e-741f0b344731 + # + # There's no sigdata for 8762b. c8a93 is from before changing the file. + if [ "${SWUPD_PINNED_PUBKEY_HASH}" ]; then + install -d ${IMAGE_ROOTFS}${datadir}/clear/update-ca + install -m 0644 '${SWUPD_PINNED_PUBKEY}' ${IMAGE_ROOTFS}${datadir}/clear/update-ca/ + echo "${datadir}/clear/update-ca/$(basename '${SWUPD_PINNED_PUBKEY}')" > ${IMAGE_ROOTFS}/usr/share/defaults/swupd/pinnedpubkey + fi + chown -R root:root ${IMAGE_ROOTFS}/usr/share/defaults/swupd + chmod 0644 ${IMAGE_ROOTFS}/usr/share/defaults/swupd/* +} +ROOTFS_POSTPROCESS_COMMAND_append = " configure_swupd_client;" diff --git a/classes/swupd-image.bbclass b/classes/swupd-image.bbclass index 645adab..dd56a34 100644 --- a/classes/swupd-image.bbclass +++ b/classes/swupd-image.bbclass @@ -15,6 +15,8 @@ # # See docs/Guide.md for more information. +inherit swupd-client + # Created for each bundle (including os-core) and the "full" directory, # describing files and directories that swupd-server needs to include in the update # mechanism (i.e. without SWUPD_FILE_BLACKLIST entries). Used by swupd-server. @@ -32,69 +34,6 @@ SWUPD_IMAGE_PN = "${@ d.getVar('PN_BASE', True) or d.getVar('PN', True)}" # to be published will be in the "www" sub-directory. DEPLOY_DIR_SWUPD = "${DEPLOY_DIR}/swupd/${MACHINE}/${SWUPD_IMAGE_PN}" -# The "format" needs to be bumped for different reasons: -# - the output of the swupd-server changes in a way that -# a swupd-client currently installed on devices will not -# understand it (example: changing file names or using -# a new compression method for archives) -# - the content of the distro changes such that a device -# cannot update directly to the latest build (example: -# the distro changes the boot loader and some swupd postinst -# helper which knows about that change must be installed on -# the device first before actually switching) -# -# meta-swupd handles the first case with SWUPD_TOOLS_FORMAT. -# The default value matches the default versions of the swupd-server -# and swupd-client. Distros can override this if they need to pick -# non-default versions of the tools, but that is not tested. -# -# Distros need to handle the second case by preparing and releasing -# a build that devices can update to (i.e. the version URL the devices -# check must have that update), then make the incompatible change and -# in the next build bump the SWUPD_DISTRO_FORMAT. -# -# In both cases, SWUPD_FORMAT gets bumped. meta-swupd notices that -# and then prepares a special transitional update: -# - the rootfs is configured to use the new SWUUPD_FORMAT and -# OS_VERSION -# - a fake OS_VERSION-1 release is built using a swupd-server that is -# compatible with the swupd-client before the bump -# - the OS_VERSION release then is the first one using the new format -# -# This way, devices are forced to update to OS_VERSION-1 because that -# will forever be the "latest" version for their current format. -# Once they have updated, the device really is on OS_VERSION, configured -# to use the new format, and the next update check will see future -# releases again. -# -# For this to work, "swupd-client" should always be invoked without -# explicit format parameter. -SWUPD_TOOLS_FORMAT ?= "4" -SWUPD_DISTRO_FORMAT ?= "0" -SWUPD_FORMAT = "${@ str(int('${SWUPD_TOOLS_FORMAT}') + int('${SWUPD_DISTRO_FORMAT}')) }" -IMAGE_INSTALL_append = " swupd-client-format${SWUPD_TOOLS_FORMAT}" - -# The information about where to find version information and actual -# content is needed in several places: -# - the swupd client in the image gets configured such that it uses that as default -# - swupd server needs information about the previous build -# -# The version URL determines what the client picks as the version that it updates to. -# The content URL must have all builds ever produced and is expected to also -# have the corresponding version information. -# -# To build the very first version of an image, set these to empty. -# Errors while accessing the server (as the non-existent download.example.com) -# or not having any previous build on that server are fatal. The latter -# is necessary to detect misconfiguration. -SWUPD_VERSION_URL ??= "http://download.example.com/updates/my-distro/milestone/${MACHINE}/${SWUPD_IMAGE_PN}" -SWUPD_CONTENT_URL ??= "http://download.example.com/updates/my-distro/builds/${MACHINE}/${SWUPD_IMAGE_PN}" - -# An absolute path for a file containing the SSL certificate that is -# is to be used for verifying https connections to the version and content -# derver. -SWUPD_PINNED_PUBKEY ??= "" - # User configurable variables to disable all swupd processing or deltapack # generation. SWUPD_GENERATE ??= "1" @@ -102,13 +41,9 @@ SWUPD_DELTAPACK_VERSIONS ??= "" SWUPD_LOG_FN ??= "bbdebug 1" -# This version number *must* map to VERSION_ID in /etc/os-release and *must* be -# a non-negative integer that fits in an int. -OS_VERSION ??= "${DISTRO_VERSION}" - # When doing format changes, this version number is used for the intermediate # release. Default is OS_VERSION - 1. There's a separate sanity check for -# OS_VERSION below, so this code should always work. +# OS_VERSION in the .inc file, so this code should always work. OS_VERSION_INTERIM ?= "${@ ${OS_VERSION} - 1 }" # We need to preserve xattrs, which works with bsdtar out of the box. @@ -125,14 +60,6 @@ REQUIRED_DISTRO_FEATURES = "systemd" python () { import os - ver = d.getVar('OS_VERSION', True) or 'invalid' - try: - ver = int(ver) - except ValueError: - bb.fatal("Invalid value for OS_VERSION (%s), must be a non-negative integer value." % ver) - if ver <= 0 or ver > 2147483647: - bb.fatal('OS_VERSION outside of valid range (> 0, <= 2147483647): %d' % ver) - havebundles = (d.getVar('SWUPD_BUNDLES', True) or '') != '' deploy_dir = d.getVar('DEPLOY_DIR_SWUPD', True) @@ -642,26 +569,6 @@ python swupd_replace_hardlinks () { } ROOTFS_POSTPROCESS_COMMAND += "swupd_replace_hardlinks; " -# swupd-client checks VERSION_ID, which must match the OS_VERSION -# used for generating swupd bundles in the current build. -# -# We patch this during image creation and exclude OS_VERSION from the -# dependencies because doing it during the compilation of os-release.bb -# would trigger a rebuild even if all that changed is the OS_VERSION. -# It would also affect builds of images where swupd is not active. Both -# is undesirable. -# -# If triggering a rebuild on each OS_VERSION change is desired, -# then this can be achieved by influencing the os-release package -# by setting in local.conf: -# VERSION_ID = "${OS_VERSION}" -IMAGE_INSTALL_append = " os-release" -swupd_patch_os_release () { - sed -i -e 's/^VERSION_ID *=.*/VERSION_ID="${OS_VERSION}"/' ${IMAGE_ROOTFS}/usr/lib/os-release -} -swupd_patch_os_release[vardepsexclude] = "OS_VERSION" -ROOTFS_POSTPROCESS_COMMAND += "swupd_patch_os_release; " - # Check whether the constructed image contains any dangling symlinks, these # are likely to indicate deeper issues. # NOTE: you'll almost certainly want to override these for your distro. @@ -721,51 +628,3 @@ i.e. the link is to a file which only exists at runtime, such as files in /proc, SWUPD_IMAGE_SYMLINK_WHITELIST to resolve this error.' raise ImageQAFailed(message, swupd_check_dangling_symlinks) } - -def hash_swupd_pinned_pubkey(d): - pubkey = d.getVar('SWUPD_PINNED_PUBKEY', True) - if pubkey: - import hashlib - bb.parse.mark_dependency(d, pubkey) - with open(pubkey, 'rb') as f: - hash = hashlib.sha256() - hash.update(f.read()) - return hash.hexdigest() - else: - return '' - -SWUPD_PINNED_PUBKEY_HASH := "${@ hash_swupd_pinned_pubkey(d)}" - -# The swupd client must be configured on a per-image basis. -# Different images might need different settings. -configure_swupd_client () { - # Write default values to the configuration hierarchy (since 3.4.0) - install -d ${IMAGE_ROOTFS}/usr/share/defaults/swupd - echo "${SWUPD_VERSION_URL}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/versionurl - echo "${SWUPD_CONTENT_URL}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/contenturl - echo "${SWUPD_FORMAT}" >> ${IMAGE_ROOTFS}/usr/share/defaults/swupd/format - # Changing content of the pubkey also changes the hash and thus ensures - # that this method and thus do_rootfs run again. - # - # TODO: does not actually work. Recipe gets reparsed when the file - # changes ("bitbake -e ostro-image-swupd | SWUPD_PINNED_PUBKEY_HASH" changes) - # but the task does not get re-executed. Forcing that leads to: - # - # ERROR: ostro-image-swupd-1.0-r0 do_rootfs: Taskhash mismatch 8762bf20b997ac29dd6793fd11e609c3 versus cb40afac8ca291e31022d5ffd9a9bbac for /work/ostro-os/meta-ostro/recipes-image/images/ostro-image-swupd.bb.do_rootfs - # ERROR: Taskhash mismatch 8762bf20b997ac29dd6793fd11e609c3 versus cb40afac8ca291e31022d5ffd9a9bbac for /work/ostro-os/meta-ostro/recipes-image/images/ostro-image-swupd.bb.do_rootfs - # - # $ bitbake-diffsigs tmp-glibc/stamps/qemux86-ostro-linux/ostro-image-swupd/1.0-r0.do_rootfs.sigdata.c8a9371831f58ce4f8b49a73211f66aa tmp-glibc/stamps/qemux86-ostro-linux/ostro-image-swupd/1.0-r0.do_rootfs.sigdata.cb40afac8ca291e31022d5ffd9a9bbac - # basehash changed from 02de100ee7baa348e224f21844fdaa06 to e3bb23a069673a09afee4994522991d3 - # Variable SWUPD_PINNED_PUBKEY_HASH value changed from 'b9ffbe0963f3f7ab3f3c1af5cd8471c121cb601eb4294ad4b211f1e206746a0a' to '8d172423eb0162feb8c7fb2f2d7da28a6effdf3e95184114c62e6b0efdeae89a' - # Taint (by forced/invalidated task) changed from None to 2c8e3b43-5e70-4c96-bf6e-741f0b344731 - # - # There's no sigdata for 8762b. c8a93 is from before changing the file. - if [ "${SWUPD_PINNED_PUBKEY_HASH}" ]; then - install -d ${IMAGE_ROOTFS}${datadir}/clear/update-ca - install -m 0644 '${SWUPD_PINNED_PUBKEY}' ${IMAGE_ROOTFS}${datadir}/clear/update-ca/ - echo "${datadir}/clear/update-ca/$(basename '${SWUPD_PINNED_PUBKEY}')" > ${IMAGE_ROOTFS}/usr/share/defaults/swupd/pinnedpubkey - fi - chown -R root:root ${IMAGE_ROOTFS}/usr/share/defaults/swupd - chmod 0644 ${IMAGE_ROOTFS}/usr/share/defaults/swupd/* -} -ROOTFS_POSTPROCESS_COMMAND_append = " configure_swupd_client;" diff --git a/conf/swupd/swupd-config.inc b/conf/swupd/swupd-config.inc new file mode 100644 index 0000000..bb9eaa5 --- /dev/null +++ b/conf/swupd/swupd-config.inc @@ -0,0 +1,55 @@ +# The "format" needs to be bumped for different reasons: +# - the output of the swupd-server changes in a way that +# a swupd-client currently installed on devices will not +# understand it (example: changing file names or using +# a new compression method for archives) +# - the content of the distro changes such that a device +# cannot update directly to the latest build (example: +# the distro changes the boot loader and some swupd postinst +# helper which knows about that change must be installed on +# the device first before actually switching) +# +# meta-swupd handles the first case with SWUPD_TOOLS_FORMAT. +# The default value matches the default versions of the swupd-server +# and swupd-client. Distros can override this if they need to pick +# non-default versions of the tools, but that is not tested. +# +# Distros need to handle the second case by preparing and releasing +# a build that devices can update to (i.e. the version URL the devices +# check must have that update), then make the incompatible change and +# in the next build bump the SWUPD_DISTRO_FORMAT. +# +# In both cases, SWUPD_FORMAT gets bumped. meta-swupd notices that +# and then prepares a special transitional update: +# - the rootfs is configured to use the new SWUUPD_FORMAT and +# OS_VERSION +# - a fake OS_VERSION-1 release is built using a swupd-server that is +# compatible with the swupd-client before the bump +# - the OS_VERSION release then is the first one using the new format +# +# This way, devices are forced to update to OS_VERSION-1 because that +# will forever be the "latest" version for their current format. +# Once they have updated, the device really is on OS_VERSION, configured +# to use the new format, and the next update check will see future +# releases again. +# +# For this to work, "swupd-client" should always be invoked without +# explicit format parameter. +SWUPD_TOOLS_FORMAT ?= "4" +SWUPD_DISTRO_FORMAT ?= "0" +SWUPD_FORMAT = "${@ str(int('${SWUPD_TOOLS_FORMAT}') + int('${SWUPD_DISTRO_FORMAT}')) }" + + +# This version number *must* map to VERSION_ID in /etc/os-release and *must* be +# a non-negative integer that fits in an int. +OS_VERSION ??= "${DISTRO_VERSION}" + +python () { + ver = d.getVar('OS_VERSION', True) or 'invalid' + try: + ver = int(ver) + except ValueError: + bb.fatal("Invalid value for OS_VERSION (%s), must be a non-negative integer value." % ver) + if ver <= 0 or ver > 2147483647: + bb.fatal('OS_VERSION outside of valid range (> 0, <= 2147483647): %d' % ver) +} |