#!/bin/bash # create the system seed for TPM-backed FDE in the live layer of the installer. set -eux case ${PASS:-} in *.live) ;; *) exit 0 ;; esac . config/binary . config/functions # Naive conversion from YAML to JSON. This is needed because yq is in universe # (but jq is not). yaml_to_json() { python3 -c ' import json import sys import yaml json.dump(yaml.safe_load(sys.stdin), sys.stdout, default=str) ' } # Use jq to retrieve a list of --snap options from a given *signed* model. get_snaps_args_excluding() { local model=$1 local jq_filter=' # Find all snaps that are not filtered out. # The filtered out snaps are passed as positional arguments so they end up in # the $ARGS.positional array. .snaps[] | select(.name | IN($ARGS.positional[]) | not) # Then forge the --snap option. | "--snap=" + .name + "=" + .["default-channel"]' shift # The model is signed and is not valid YAML unless we get rid of the # signature. Here we assume the only blank line is before the signature. sed '/^$/,$d' -- "$model" \ | yaml_to_json \ | jq --raw-output "$jq_filter" --args "$@" } # Use jq to retrieve a list of --snap options from a given *signed* model. get_snaps_args() { local model=$1 get_snaps_args_excluding "$model" } _get_components_filtered() { local excluded=$1 local model=$2 local jq_filter=' # Find all snaps that are either filtered in or filtered out # The filtered in (or out) snaps are passed as positional arguments so they end up in # the $ARGS.positional array. The excluded variable is passed separately and # tells if we want to filter in (i.e., excluded=false) or filter out (i.e., # excluded=true). .snaps[] | select(.name | IN($ARGS.positional[]) | if $excluded then not else . end) # and have components | select(.components) # Then save the name of each snap in a variable | .name as $snap # Then for each entry that has "optional" | .components | to_entries | map(select(.value.presence == "optional")) # Output its name with the snap name prepended | "\($snap)" + "+" + .[].key' shift 2 sed '/^$/,$d' -- "$model" \ | yaml_to_json \ | jq --raw-output "$jq_filter" --argjson excluded "$excluded" --args "$@" } # Get list of all components for all snaps get_all_components() { local model=$1 # Provide an exclusion list but empty _get_components_filtered true "$model" } # Get list of all components for all snaps except the ones specified. get_components_excluding() { local model=$1 shift _get_components_filtered true "$model" "$@" } # Get list of all components for the snaps specified. get_components() { local model=$1 shift _get_components_filtered false "$model" "$@" } # Generation of the model: # * At https://github.com/canonical/models one can find a repo of raw, # unsigned, input .json files, and their signed .model equivalents. # * At least once per cycle, update the json for the new Ubuntu version. # To do this, take the previous cycle ubuntu-classic-$ver-amd64.json file, # rename for the new version, and do any necessary updates including fixing # the versions of tracks. # * When this is done, the json needs to be signed. This needs to be done by # a Canonical employee - try asking someone who has recently opened PRs on # https://github.com/canonical/models with the signed models. # * Ensure the signed and unsigned version of the models are updated in the # models repo. # * The signed model can then be placed here in livecd-rootfs at # live-build/${PROJECT}/ubuntu-classic-amd64.model # env SNAPPY_STORE_NO_CDN=1 snap known --remote model series=16 brand-id=canonical model=ubuntu-classic-2410-amd64 > config/classic-model.model # dangerous_model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64-dangerous.model stable_model=/usr/share/livecd-rootfs/live-build/${PROJECT}/ubuntu-classic-amd64.model prepare_args=() components=() # for the dangerous subproject, we need the dangerous model! if [ "$SUBPROJECT" = "dangerous" ]; then # As with the "classically" seeded snaps, snaps from the edge channel may # require different content snaps to be installed, so they must be # included in the system as well. We just use the same list as was # computed in snap_validate_seed. model="${dangerous_model}" while read snap; do prepare_args+=("--snap=${snap}=edge") done < config/missing-providers for comp in $(get_all_components "$model"); do components+=("$comp") done else # Normally we use the stable model here. Use the dangerous one for now # until we get snaps on stable 26.04 tracks and channels. #model="${stable_model}" model="${dangerous_model}" # We're currently using the dangerous model for the stable image because it # allows us to override snaps. But we don't want all snaps from edge like # the dangerous model has, we want most of them from stable excluding: # * snapd (for TPM/FDE) # * snapd-desktop-integration (for TPM/FDE) # * firmware-updater (for TPM/FDE) # * desktop-security-center (for TPM/FDE) snaps_from_dangerous=(snapd snapd-desktop-integration firmware-updater desktop-security-center) for snap_arg in $(get_snaps_args_excluding "$stable_model" "${snaps_from_dangerous[@]}"); do prepare_args+=("$snap_arg") done for comp in $(get_components_excluding "$stable_model" "${snaps_from_dangerous[@]}"); do components+=("$comp") done for comp in $(get_components "$dangerous_model" "${snaps_from_dangerous[@]}"); do components+=("$comp") done fi for comp in "${components[@]}"; do prepare_args+=(--comp "$comp") done channel="" if [ -n "${CHANNEL:-}" ]; then channel="--channel $CHANNEL" fi # Set UBUNTU_STORE_COHORT_KEY="+" to force prepare-image to fetch the latest # snap versions regardless of phasing status env SNAPPY_STORE_NO_CDN=1 UBUNTU_STORE_COHORT_KEY="+" snap prepare-image \ --classic $model $channel "${prepare_args[@]}" chroot mv chroot/system-seed/systems/* chroot/system-seed/systems/enhanced-secureboot-desktop rsync -av chroot/system-seed/{systems,snaps} chroot/var/lib/snapd/seed rm -rf chroot/system-seed/