From f9020f842cb09ecee509ef472bdf2e28254c01f7 Mon Sep 17 00:00:00 2001 From: Eero Kelly Date: Thu, 16 Jun 2022 18:36:31 +0000 Subject: [PATCH] SetupOS 2 --- .gitignore | 1 + .../53--host-os-build--build-setupos.yml | 17 +- .../53--host-os-build--setupos-base-image.yml | 33 ++ .../src/job_scripts/lib/setup-os-diskimg.sh | 49 +++ ic-os/setupos/Makefile | 8 - ic-os/setupos/README.md | 79 ---- ic-os/setupos/bootloader/Dockerfile | 17 + ic-os/setupos/bootloader/grub.cfg | 31 ++ ic-os/setupos/bootloader/grubenv | 2 + ic-os/setupos/containerfs/.dockerignore | 1 + ic-os/setupos/containerfs/Dockerfile | 30 ++ ic-os/setupos/containerfs/README.adoc | 24 ++ ic-os/setupos/containerfs/etc/ssh/.gitignore | 2 + .../systemd-logind.service.d/override.conf | 5 + ic-os/setupos/extra_boot_args | 14 + ic-os/setupos/rootfs/Dockerfile | 82 ++++ ic-os/setupos/rootfs/Dockerfile.base | 34 ++ ic-os/setupos/rootfs/README.adoc | 183 ++++++++ ic-os/setupos/rootfs/docker-base | 1 + ic-os/setupos/rootfs/etc/default/locale | 1 + ic-os/setupos/rootfs/etc/fstab | 8 + ic-os/setupos/rootfs/etc/hostname | 1 + ic-os/setupos/rootfs/etc/hosts | 8 + .../systemd-gpt-auto-generator | 6 + .../rootfs/etc/systemd/system/setupos.service | 12 + .../opt/ic/bin/devices.sh} | 47 +- .../05_disk.sh => rootfs/opt/ic/bin/disk.sh} | 6 +- .../opt/ic/bin/firmware.sh} | 6 +- .../opt/ic/bin/functions.sh} | 50 ++- .../opt/ic/bin/guestos.sh} | 8 +- .../opt/ic/bin/hardware.sh} | 6 +- .../opt/ic/bin/hostos.sh} | 8 +- ic-os/setupos/rootfs/opt/ic/bin/setupos.sh | 139 ++++++ .../04_uefi.sh => rootfs/opt/ic/bin/uefi.sh} | 2 +- ic-os/setupos/rootfs/packages.common | 44 ++ ic-os/setupos/rootfs/packages.dev | 24 ++ .../prep/fscontext-fixes/fscontext-fixes.fc | 9 + .../prep/fscontext-fixes/fscontext-fixes.if} | 0 .../prep/fscontext-fixes/fscontext-fixes.te | 19 + .../rootfs/prep/misc-fixes/misc-fixes.if | 0 .../rootfs/prep/misc-fixes/misc-fixes.te | 31 ++ ic-os/setupos/rootfs/prep/prep.sh | 20 + .../prep/systemd-fixes/systemd-fixes.if | 41 ++ .../prep/systemd-fixes/systemd-fixes.te | 126 ++++++ ic-os/setupos/scripts/artifact-utils.sh | 18 + ic-os/setupos/scripts/build-disk-image.sh | 321 ++++++++++++++ ic-os/setupos/scripts/build-iso.sh | 400 ------------------ ic-os/setupos/scripts/get-artifact-version.sh | 9 + ic-os/setupos/scripts/partitions.csv | 20 + ic-os/setupos/scripts/partitions.sh | 97 +++++ ic-os/setupos/shell.nix | 17 + ic-os/setupos/src/nocloud/01_setupos.sh | 31 -- ic-os/setupos/src/nocloud/09_setupos.sh | 48 --- ic-os/setupos/src/nocloud/deployment.json | 17 - ic-os/setupos/src/nocloud/nns_public_key.pem | 5 - ic-os/setupos/src/nocloud/user-data | 26 -- toolchains/sysimage/build_disk_image.py | 4 +- toolchains/sysimage/build_fat32_image.py | 163 +++++++ toolchains/sysimage/docker_tar.py | 6 +- 59 files changed, 1739 insertions(+), 678 deletions(-) create mode 100644 gitlab-ci/config/53--host-os-build--setupos-base-image.yml create mode 100755 gitlab-ci/src/job_scripts/lib/setup-os-diskimg.sh delete mode 100755 ic-os/setupos/Makefile delete mode 100644 ic-os/setupos/README.md create mode 100644 ic-os/setupos/bootloader/Dockerfile create mode 100644 ic-os/setupos/bootloader/grub.cfg create mode 100644 ic-os/setupos/bootloader/grubenv create mode 100644 ic-os/setupos/containerfs/.dockerignore create mode 100644 ic-os/setupos/containerfs/Dockerfile create mode 100644 ic-os/setupos/containerfs/README.adoc create mode 100644 ic-os/setupos/containerfs/etc/ssh/.gitignore create mode 100644 ic-os/setupos/containerfs/etc/systemd/system/systemd-logind.service.d/override.conf create mode 100644 ic-os/setupos/extra_boot_args create mode 100644 ic-os/setupos/rootfs/Dockerfile create mode 100644 ic-os/setupos/rootfs/Dockerfile.base create mode 100644 ic-os/setupos/rootfs/README.adoc create mode 100644 ic-os/setupos/rootfs/docker-base create mode 100644 ic-os/setupos/rootfs/etc/default/locale create mode 100644 ic-os/setupos/rootfs/etc/fstab create mode 100644 ic-os/setupos/rootfs/etc/hostname create mode 100644 ic-os/setupos/rootfs/etc/hosts create mode 100755 ic-os/setupos/rootfs/etc/systemd/system-generators/systemd-gpt-auto-generator create mode 100644 ic-os/setupos/rootfs/etc/systemd/system/setupos.service rename ic-os/setupos/{src/nocloud/08_devices.sh => rootfs/opt/ic/bin/devices.sh} (59%) rename ic-os/setupos/{src/nocloud/05_disk.sh => rootfs/opt/ic/bin/disk.sh} (93%) rename ic-os/setupos/{src/nocloud/03_firmware.sh => rootfs/opt/ic/bin/firmware.sh} (90%) rename ic-os/setupos/{src/nocloud/00_common.sh => rootfs/opt/ic/bin/functions.sh} (62%) rename ic-os/setupos/{src/nocloud/07_guestos.sh => rootfs/opt/ic/bin/guestos.sh} (81%) rename ic-os/setupos/{src/nocloud/02_hardware.sh => rootfs/opt/ic/bin/hardware.sh} (97%) rename ic-os/setupos/{src/nocloud/06_hostos.sh => rootfs/opt/ic/bin/hostos.sh} (92%) create mode 100755 ic-os/setupos/rootfs/opt/ic/bin/setupos.sh rename ic-os/setupos/{src/nocloud/04_uefi.sh => rootfs/opt/ic/bin/uefi.sh} (92%) create mode 100644 ic-os/setupos/rootfs/packages.common create mode 100644 ic-os/setupos/rootfs/packages.dev create mode 100644 ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.fc rename ic-os/setupos/{src/nocloud/meta-data => rootfs/prep/fscontext-fixes/fscontext-fixes.if} (100%) create mode 100644 ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.te create mode 100644 ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.if create mode 100644 ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.te create mode 100755 ic-os/setupos/rootfs/prep/prep.sh create mode 100644 ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.if create mode 100644 ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.te create mode 100755 ic-os/setupos/scripts/artifact-utils.sh create mode 100755 ic-os/setupos/scripts/build-disk-image.sh delete mode 100755 ic-os/setupos/scripts/build-iso.sh create mode 100755 ic-os/setupos/scripts/get-artifact-version.sh create mode 100644 ic-os/setupos/scripts/partitions.csv create mode 100755 ic-os/setupos/scripts/partitions.sh create mode 100644 ic-os/setupos/shell.nix delete mode 100755 ic-os/setupos/src/nocloud/01_setupos.sh delete mode 100755 ic-os/setupos/src/nocloud/09_setupos.sh delete mode 100644 ic-os/setupos/src/nocloud/deployment.json delete mode 100644 ic-os/setupos/src/nocloud/nns_public_key.pem delete mode 100644 ic-os/setupos/src/nocloud/user-data create mode 100755 toolchains/sysimage/build_fat32_image.py diff --git a/.gitignore b/.gitignore index f74503fb59c..e6204247a90 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ test_report.xml # IC-OS disk images *-os.iso +*os.img *-img.tar.gz *-img.tar.zst diff --git a/gitlab-ci/config/53--host-os-build--build-setupos.yml b/gitlab-ci/config/53--host-os-build--build-setupos.yml index c860418a064..393f48b2f5a 100644 --- a/gitlab-ci/config/53--host-os-build--build-setupos.yml +++ b/gitlab-ci/config/53--host-os-build--build-setupos.yml @@ -12,21 +12,22 @@ setup-os-iso: - | set -xeuo pipefail VERSION=$(git rev-parse HEAD) + export VERSION echo "Build ID: ${VERSION}" + cd "${CI_PROJECT_DIR}"/ic-os/setupos + # NOTE: This is currently hardcoded to a specific blessed version of guestOS "$CI_PROJECT_DIR"/gitlab-ci/src/artifacts/rclone_download.py --remote-path=guest-os --out=guestos --git-rev="${VERSION}" "$CI_PROJECT_DIR"/gitlab-ci/src/artifacts/rclone_download.py --remote-path=host-os --out=hostos --git-rev="${VERSION}" - BUILD_OUT="ic-os/setupos/build-out" - UPLOAD_TARGET="setup-os" - - ic-os/setupos/scripts/build-iso.sh \ - --guest-os=./guestos/disk-img/disk-img.tar.gz \ - --host-os=./hostos/disk-img/host-disk-img.tar.gz \ - --output="$BUILD_OUT" + BUILD_OUT="build-out/disk-img" + BUILD_TMP="build-tmp" + UPLOAD_TARGET="setup-os/disk-img" - "$CI_PROJECT_DIR"/gitlab-ci/src/artifacts/openssl-sign.sh "$BUILD_OUT" + buildevents cmd "${ROOT_PIPELINE_ID}" "${CI_JOB_ID}" build-host-img -- \ + "${CI_PROJECT_DIR}"/gitlab-ci/src/job_scripts/lib/setup-os-diskimg.sh \ + "$BUILD_OUT" "$BUILD_TMP" "$UPLOAD_TARGET" "$VERSION" buildevents cmd "$ROOT_PIPELINE_ID" "$CI_JOB_ID" rclone -- \ "${CI_PROJECT_DIR}"/gitlab-ci/src/artifacts/rclone_upload.py --version="${VERSION}" "$BUILD_OUT" "$UPLOAD_TARGET" diff --git a/gitlab-ci/config/53--host-os-build--setupos-base-image.yml b/gitlab-ci/config/53--host-os-build--setupos-base-image.yml new file mode 100644 index 00000000000..89ce091824a --- /dev/null +++ b/gitlab-ci/config/53--host-os-build--setupos-base-image.yml @@ -0,0 +1,33 @@ +deploy-setup-os-baseimg: + extends: + - .ubuntu-nix-docker-protected + - .rules-protected-branch-manual + stage: host-os-build + needs: [] + script: + - | + set -euo pipefail + + TAG=$(date '+%Y-%m-%d-%H%M') + + docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD" + cd "${CI_PROJECT_DIR}/ic-os/setupos/rootfs" + docker build -q -t dfinity/setupos-base:"$TAG" -t dfinity/setupos-base:latest -f Dockerfile.base . + docker push dfinity/setupos-base:"$TAG" + + echo "Use the image with it's SHA256 DIGEST below for IC-OS Dockerfile" + docker inspect --format='{{index .RepoDigests 0}}' dfinity/setupos-base:"$TAG" + +build-setup-os-baseimg: + extends: + - .cargo-build-docker + - .rules-dockerfile-base-changes + stage: host-os-build + needs: [] + script: + - | + set -euo pipefail + + TAG=$(date '+%Y-%m-%d-%H%M') + cd "${CI_PROJECT_DIR}/ic-os/setupos/rootfs" + docker build -t dfinity/setupos-base:"$TAG" -t dfinity/setupos-base:latest -f Dockerfile.base . diff --git a/gitlab-ci/src/job_scripts/lib/setup-os-diskimg.sh b/gitlab-ci/src/job_scripts/lib/setup-os-diskimg.sh new file mode 100755 index 00000000000..90f51707b37 --- /dev/null +++ b/gitlab-ci/src/job_scripts/lib/setup-os-diskimg.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# +# Script for setup-os-diskimg CI job +# + +set -euo pipefail + +BUILD_OUT=${1:-"build-out/disk-img"} +BUILD_TMP=${2:-"build-tmp"} +UPLOAD_TARGET=${3:-"setup-os/disk-img"} +VERSION=${4:-$(git rev-parse --verify HEAD)} + +ROOT_DIR=$(git rev-parse --show-toplevel) +ls -lah /var/run/docker.sock +groups + +cd "$ROOT_DIR" + +cd "$ROOT_DIR"/ic-os/setupos +mkdir -p "$BUILD_OUT" "$BUILD_TMP" +echo "$VERSION" >"${BUILD_TMP}/version.txt" + +# XXX Temporarily build docker image +cd rootfs +docker build -f Dockerfile.base -t dfinity/setupos-base:local . +cd .. + +if [ -z "$CI_JOB_ID" ]; then + ./scripts/build-disk-image.sh "-o=${BUILD_TMP}/disk.img" "-v=$VERSION" "--host-os=./hostos/disk-img/host-disk-img.tar.gz" "--guest-os=./guestos/disk-img/disk-img.tar.gz" + tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2020-01-01' --sparse \ + -cvzf "${BUILD_OUT}/disk-img.tar.gz" -C "$BUILD_TMP" disk.img version.txt + tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2020-01-01' --sparse \ + -cvf "${BUILD_OUT}/disk-img.tar.zst" --use-compress-program="zstd --threads=0 -10" \ + -C "$BUILD_TMP" disk.img version.txt + ls -lah "$BUILD_TMP" +else + buildevents cmd "${ROOT_PIPELINE_ID}" "${CI_JOB_ID}" build-disk-img -- \ + ./scripts/build-disk-image.sh "-o=${BUILD_TMP}/disk.img" "-v=$VERSION" "--host-os=./hostos/disk-img/host-disk-img.tar.gz" "--guest-os=./guestos/disk-img/disk-img.tar.gz" + buildevents cmd "$ROOT_PIPELINE_ID" "$CI_JOB_ID" tar-build-out -- \ + tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2020-01-01' --sparse \ + -cvzf "${BUILD_OUT}/disk-img.tar.gz" -C "$BUILD_TMP" disk.img version.txt + buildevents cmd "$ROOT_PIPELINE_ID" "$CI_JOB_ID" tar-build-out -- \ + tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2020-01-01' --sparse \ + -cvf "${BUILD_OUT}/disk-img.tar.zst" --use-compress-program="zstd --threads=0 -10" \ + -C "$BUILD_TMP" disk.img version.txt + ls -lah "$BUILD_TMP" + + "$ROOT_DIR"/gitlab-ci/src/artifacts/openssl-sign.sh "$BUILD_OUT" +fi diff --git a/ic-os/setupos/Makefile b/ic-os/setupos/Makefile deleted file mode 100755 index 356c6408c17..00000000000 --- a/ic-os/setupos/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -help: - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -deps: ## Install build dependencies - sudo apt -y install ca-certificates curl git isolinux mtools p7zip-full syslinux xorriso --no-install-recommends - -iso: ## Build SetupOS ISO image - ./scripts/build-iso.sh diff --git a/ic-os/setupos/README.md b/ic-os/setupos/README.md deleted file mode 100644 index e51c75fecdb..00000000000 --- a/ic-os/setupos/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Setup OS - -## Index - -* [About](#about) - * [Features](#features) -* [Build](#build) - * [Dependencies](#dependencies) -* [Usage](#usage) - * [ISO](#iso) - * [MaaS](#maas) -* [FAQ](#faq) -* [Appendix](#appendix) - -## About - -This folder holds all files to build the SetupOS ISO and MaaS image. - -### Features - -* Set firmware versions -* Apply UEFI configuration -* Purge existing partitions -* Create new partitions -* Install HostOS disk-image -* Install GuestOS disk-image -* Handle HSM and USB devices - -## Build - -### Dependencies - -To build the Ubuntu Server 20.04 LTS based images, the following dependencies -have to be met. Please note that the script currently only supports Ubuntu -Linux. - -* Operating System: Ubuntu 20.04.3 -* Packages: `ca-certificates`, `curl`, `git`, `isolinux`, `p7zip-full`, - `syslinux`, `xorriso` -* Connectivity: 443/tcp outbound - -## Usage - -Executing the Bash scripts in the _scripts_ folder, is enough to get the build -process started. - -### ISO - -Command to build the ISO image: -``` -./scripts/build-iso.sh -``` - -Once the build process has finished successfully, the SetupOS ISO image can be -found in the _./build-out/_ folder. - -For help and listing the available options, simply append the _--help_ flag: -``` -./scripts/build-iso.sh --help -``` - -### MaaS - -Command to build the MaaS image: -``` -./scripts/build-maas.sh -``` - -Once the build process has finished successfully, the SetupOS MaaS images can be -found in the _./build-out/_ folder. - -For help and listing the available options, simply append the _--help_ flag: -``` -./scripts/build-maas.sh --help -``` - -## FAQ - -## Appendix diff --git a/ic-os/setupos/bootloader/Dockerfile b/ic-os/setupos/bootloader/Dockerfile new file mode 100644 index 00000000000..f3edb8e95ed --- /dev/null +++ b/ic-os/setupos/bootloader/Dockerfile @@ -0,0 +1,17 @@ +# 20.04 +FROM ubuntu:focal-20211006 + +RUN apt-get -y update && apt-get -y upgrade && apt-get -y --no-install-recommends install \ + grub-efi-amd64-bin faketime + +# Copy all grub modules into their requisite place +RUN mkdir -p /boot/grub ; cp -r /usr/lib/grub/x86_64-efi /boot/grub + +# Build grub image itself into EFI directory tree +RUN mkdir -p /boot/efi/EFI/Boot +RUN faketime "1970-1-1 0" grub-mkimage -p "(,gpt2)/" -O x86_64-efi -o /boot/efi/EFI/Boot/bootx64.efi \ + boot linux search normal configfile \ + part_gpt btrfs ext2 fat iso9660 loopback \ + test keystatus gfxmenu regexp probe \ + efi_gop efi_uga all_video gfxterm font \ + echo read ls cat png jpeg halt reboot loadenv \ diff --git a/ic-os/setupos/bootloader/grub.cfg b/ic-os/setupos/bootloader/grub.cfg new file mode 100644 index 00000000000..39771f966a3 --- /dev/null +++ b/ic-os/setupos/bootloader/grub.cfg @@ -0,0 +1,31 @@ +regexp -s boot_disk '^\(([a-z0-9]*),[a-z0-9]*\)/EFI/BOOT' $cmdpath + +set prefix=($boot_disk,gpt2) + +if [ -s $prefix/grubenv ]; then + load_env +fi + +set root=($boot_disk,gpt5) +set linux_root=PARTUUID=7C0A626E-E5EA-E543-B5C5-300EB8304DB7 + +echo Booting linux... + +if [ -f ${boot}/extra_boot_args ]; then + echo Loading extra boot args ${boot}/extra_boot_args + # This is not really great -- we are directly sourcing the target + # file while we would only want a single variable out of it. + # I don't think there is another way. + source ${boot}/extra_boot_args + echo Extra boot arguments $EXTRA_BOOT_ARGS +fi + +linux /vmlinuz root=$linux_root console=ttyS0,115200 console=tty0 $EXTRA_BOOT_ARGS + +if [ -f ${boot}/initrd.img ] ; then + echo Loading initial ram disk ${boot}/initrd.img + initrd ${boot}/initrd.img +fi + + +boot diff --git a/ic-os/setupos/bootloader/grubenv b/ic-os/setupos/bootloader/grubenv new file mode 100644 index 00000000000..f93ccbfff52 --- /dev/null +++ b/ic-os/setupos/bootloader/grubenv @@ -0,0 +1,2 @@ +# GRUB Environment Block +####################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################### \ No newline at end of file diff --git a/ic-os/setupos/containerfs/.dockerignore b/ic-os/setupos/containerfs/.dockerignore new file mode 100644 index 00000000000..4bab69b849b --- /dev/null +++ b/ic-os/setupos/containerfs/.dockerignore @@ -0,0 +1 @@ +**/.gitignore diff --git a/ic-os/setupos/containerfs/Dockerfile b/ic-os/setupos/containerfs/Dockerfile new file mode 100644 index 00000000000..14f358b78af --- /dev/null +++ b/ic-os/setupos/containerfs/Dockerfile @@ -0,0 +1,30 @@ +ARG UBUNTU_ROOTFS +FROM ${UBUNTU_ROOTFS} + +# Copy in some config settings for the container -- particularly, a +# deterministic set of ssh keys to avoid annoyances like key changes during +# development. + +COPY etc /etc +RUN chmod 400 /etc/ssh/ssh_host*key ; chmod 444 /etc/ssh/ssh_host*key.pub + +# Remove everything related to filesystem mounts and encrypted filesystem setup. +# This would fail the boot when run as a docker container. +RUN rm -rf /etc/fstab /etc/systemd/system-generators /etc/crypttab + +# Disable all our newly installed services that are going to fail unless run on +# a VM (i.e. all those that set up filesystem mounts etc.). +# Leave the replica service for bootstrap and running the node intact. +# Also don't touch system-installed services (they exist as symbolic +# links in the same directory). +RUN for file in /etc/systemd/system/*.service ; do \ + svc="${file#/etc/systemd/system/}" ; \ + if [ "${svc}" != "ic-replica.service" -a "${svc}" != "bootstrap-ic-node.service" -a ! -L "/etc/systemd/system/${svc}" ]; then \ + systemctl disable "$svc" ; rm "/etc/systemd/system/${svc}" ; \ + fi ; \ + done + +# Remove dependency on this service -- it is not started in container. +RUN sed -e '/\(After\|Wants\)=systemd-networkd-wait-online/d' -i /etc/systemd/system/ic-replica.service + +ENTRYPOINT [ "/lib/systemd/systemd" ] diff --git a/ic-os/setupos/containerfs/README.adoc b/ic-os/setupos/containerfs/README.adoc new file mode 100644 index 00000000000..19e05975e13 --- /dev/null +++ b/ic-os/setupos/containerfs/README.adoc @@ -0,0 +1,24 @@ += Docker container of IC node + +This directory contains glue code required to turn the Ubuntu rootfs (normally +deployed as a VM on target systems) into a docker container. This allows +booting an entire testnet on a laptop far faster than a VM deployment. + +In order to turn the system into a docker container, all services interacting +with (virtual) devices are deactivated, and only services related to starting +a node are left intact. The "simplest possible" launch of such a container +can be performed using: + + docker run \ + --tmpfs /run \ + --tmpfs /tmp:exec \ + --tmpfs /run/lock \ + -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ + ${ID_OF_CONTAINER} + +Note that this is not _quite_ sufficient to run as a node (need +network setup and node configuration), but it gets the software stack up +and running to the point where it can be contacted via ssh and then +manually interacted with. + +See also ../scripts/build-container.sh. diff --git a/ic-os/setupos/containerfs/etc/ssh/.gitignore b/ic-os/setupos/containerfs/etc/ssh/.gitignore new file mode 100644 index 00000000000..6484a45b121 --- /dev/null +++ b/ic-os/setupos/containerfs/etc/ssh/.gitignore @@ -0,0 +1,2 @@ +ssh_host*key +ssh_host*key.pub diff --git a/ic-os/setupos/containerfs/etc/systemd/system/systemd-logind.service.d/override.conf b/ic-os/setupos/containerfs/etc/systemd/system/systemd-logind.service.d/override.conf new file mode 100644 index 00000000000..671b2e27c58 --- /dev/null +++ b/ic-os/setupos/containerfs/etc/systemd/system/systemd-logind.service.d/override.conf @@ -0,0 +1,5 @@ +# This removes delay incurred at ssh login when running as +# unprivileged docker containers (see bug +# https://github.com/systemd/systemd/issues/15408) +[Service] +ProtectHostname=no diff --git a/ic-os/setupos/extra_boot_args b/ic-os/setupos/extra_boot_args new file mode 100644 index 00000000000..31071fb3809 --- /dev/null +++ b/ic-os/setupos/extra_boot_args @@ -0,0 +1,14 @@ +# Uncomment this to run system with SELinux in PERMISSIVE mode: +# the system will use SELinux and keep track of operations that would +# be prohibited, but will only log but not actually deny them. This is +# useful for debug and policy development. The system behaves essentially the +# same as if SELinux was not activated. +# +EXTRA_BOOT_ARGS="security=selinux selinux=1 enforcing=0" + +# Uncomment this to run system with SELinux in ENFORCING mode: All rules +# of the policy are enforced, and forbidden actions are not just logged but +# stopped. This causes the system to behave differently than in either +# "no SELinux" or "permissive" mode. +# +# EXTRA_BOOT_ARGS="security=selinux selinux=1 enforcing=1" diff --git a/ic-os/setupos/rootfs/Dockerfile b/ic-os/setupos/rootfs/Dockerfile new file mode 100644 index 00000000000..46c11789acd --- /dev/null +++ b/ic-os/setupos/rootfs/Dockerfile @@ -0,0 +1,82 @@ +# SetupOS - Main Docker Image +# +# Build steps: +# - `docker build --pull -t dfinity/setupos-main -f Dockerfile .` +# +# First build stage: +# - Download 3rd party tools +# + +# The base images are defined in docker-base. Update the references there when +# a new base image has been built. Note that this argument MUST be given by the +# build script, otherwise build will fail. +ARG BASE_IMAGE= + +# +# First build stage: +# - Construct the actual target image (SetupOS root filesystem) +# + +# XXX TODO: Until this is pushed, please build the image locally +# FROM $BASE_IMAGE +FROM dfinity/setupos-base:local + +RUN mkdir -p /config \ + /data \ + /boot/efi \ + /boot/grub +COPY etc /etc + +# Update POSIX permissions in /etc/ +RUN find /etc -type d -exec chmod 0755 {} \+ && \ + find /etc -type f -not -path "/etc/hostname" -not -path "/etc/hosts" -not -path "/etc/resolv.conf" -exec chmod 0644 {} \+ && \ + chmod 0755 /etc/systemd/system-generators/* + +# Deactivate motd, it tries creating $HOME/.cache/motd.legal-displayed, +# but we want to prohibit it from writing to user home dirs +RUN sed -e '/.*pam_motd.so.*/d' -i /etc/pam.d/login + +# Deactivate systemd userdb. We don't use it. +RUN sed -e 's/ *systemd//' -i /etc/nsswitch.conf + +# Regenerate initramfs (config changed after copying in /etc) +RUN RESUME=none update-initramfs -c -k all + +RUN echo "root:$(openssl passwd -6 -salt jE8zzDEHeRg/DuGq password)" | chpasswd -e + +COPY prep /prep +RUN cd /prep && ./prep.sh && cd / && rm -rf /prep + +# All of the above sets up the base operating system. Everything below relates +# to node operation. + +RUN \ + for SERVICE in /etc/systemd/system/*; do \ + if [ -f "$SERVICE" -a ! -L "$SERVICE" ] ; then systemctl enable "${SERVICE#/etc/systemd/system/}" ; fi ; \ + done + +RUN systemctl disable systemd-resolved && \ + systemctl disable systemd-timesyncd + +# Clear all files that may lead to indeterministic build. +RUN apt-get clean && \ + rm -rf \ + /var/cache/fontconfig/* /var/cache/ldconfig/aux-cache \ + /var/log/alternatives.log /var/log/apt/history.log /var/log/apt/term.log /var/log/dpkg.log \ + /var/lib/apt/lists/* /var/lib/dbus/machine-id \ + /var/lib/initramfs-tools/5.8.0-50-generic && \ + find /usr/lib/python3.8 -name "*.pyc" | xargs rm && \ + find /usr/lib/python3 -name "*.pyc" | xargs rm && \ + find /usr/share/python3 -name "*.pyc" | xargs rm && \ + truncate --size 0 /etc/machine-id + +# Install scripts and other data late -- this means everything above +# will be cached when only the scripts change. +COPY opt /opt + +RUN mkdir /opt/ic/share + +# Update POSIX permissions in /opt/ic/ +RUN find /opt -type d -exec chmod 0755 {} \+ && \ + find /opt -type f -exec chmod 0644 {} \+ && \ + chmod 0755 /opt/ic/bin/* diff --git a/ic-os/setupos/rootfs/Dockerfile.base b/ic-os/setupos/rootfs/Dockerfile.base new file mode 100644 index 00000000000..9deb1c3a754 --- /dev/null +++ b/ic-os/setupos/rootfs/Dockerfile.base @@ -0,0 +1,34 @@ +# GuestOS - Base Image +# +# Build steps: +# - `docker build -t dfinity/setupos-base: -f Dockerfile.base .` +# - `docker push/pull dfinity/setupos-base:` +# - `docker build -t dfinity/setupos-base-dev: --build-arg PACKAGE_FILES="packages.common packages.dev" -f Dockerfile.base .` +# - `docker push/pull dfinity/setupos-base-dev:` +# +# First build stage: +# - Download and cache minimal Ubuntu Server 20.04 LTS Docker image +# - Install and cache upstream packages from built-in Ubuntu repositories +# +# NOTE! If you edit this file, you will need to perform the following +# operations to get your changes deployed. +# +# 1. Get your MR approved and merged into master +# 2. On the next hourly master pipeline, click the "deploy-setup-os-baseimg" job +# 3. Note the sha256 and update the sha256 reference in the neighboring Dockerfiles. +FROM ubuntu:20.04 + +ENV SOURCE_DATE_EPOCH=0 +ENV TZ=UTC + +# For the prod image, just use packages.common to define the packages installed +# on target. +# For the dev image, use both "packages.common" and "packages.dev" -- this can +# be set via docker build args (see above). +ARG PACKAGE_FILES=packages.common +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +COPY packages.* /tmp/ +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get -y --no-install-recommends install $(for P in ${PACKAGE_FILES}; do cat /tmp/$P | sed -e "s/#.*//" ; done) && \ + rm /tmp/packages.* diff --git a/ic-os/setupos/rootfs/README.adoc b/ic-os/setupos/rootfs/README.adoc new file mode 100644 index 00000000000..18d588a9047 --- /dev/null +++ b/ic-os/setupos/rootfs/README.adoc @@ -0,0 +1,183 @@ += Ubuntu base OS development + +The Ubuntu-based IC OS is built by: + +* creating a root filesystem image using docker -- this is based on the + official Ubuntu docker image and simply adds the OS kernel plus our + required services to it + +* converting this root filesystem into filesystem images for +/+ and +/boot+ + via +mke2fs+ + +The build instructions in the Dockerfile itself should be straight-forward +to read, required additional steps can simply be amended. + +The following template directories are simply copied verbatim onto the target +system: + +* +etc+ +* +opt+ +* +boot+ + +If you simply need to add files to the system, simply drop them into the +appropriate target directory. At present, all dfinity-specific binaries +and scripts simply go to +/opt/ic/bin+. + +If you install new systemd services, drop an appropriate unit file into +/etc/systemd/system and add an activation to the Dockerfile (maybe the +last step could be automated based on the contents of the directory). + +Various more detailed implementation aspects of the system are documented +below. + +== Dynamic file system mounts + +The exact partitions used for the +/boot+ and +/var+ filesystems depends on +what root partition the system is running from: + +* A: +/dev/vda4+ -> +/boot+, +/dev/vda5+ -> +/+, +/dev/vda6+ -> +/var+ +* B: +/dev/vda7+ -> +/boot+, +/dev/vda8+ -> +/+, +/dev/vda9+ -> +/var+ + +The system will be informed via bootloader command line whether it is +running as A or B. Since the root filesystem is conceptually supposed to +be immutable, the dynamic mappings cannot be stored in +/etc/fstab+. + +This is addressed using a generator in +/etc/systemd/system-generator/mount-generator+: +Systemd runs these during early boot, and it will dynamically generate an +appropriate +boot.mount+ unit based on the boot command-line. + +== First boot actions + +Several preparatory operations are performed when the system boots for the +first time. This documents what actions are performed presently and might +serve as a guide on how to add further actions. + +=== ssh key generation + +The +setup-ssh-keys+ (and corresponding shell script) service performs one of +two things: If this is the first boot ever (on a newly installed system), it +generates ssh keys and stashes them away in a location that is preserved across +upgrades. On first boot after an upgrade, it integrates the keys from their +storage location into the running system. The corr + +=== Encrypted data store setup + +The partition number 10 is used as encrypted LVM containing at least the ++shared-data+ LV to serve as backing store for +/var/lib/ic/data+. The +sequence to set it up is in principle not complicated, but due to the +event-driven nature of systemd must be scattered across multiple service +units and scripts: + +* Creating partition and setting up LUKS inside it: This is done by + the service +setup-encryption+ (and corresponding shell script). It creates + the partition number 10 (unless it exists already), and sets it LUKS + using a newly-generated random key. The key is stored for re-use across + reboots and upgrades. + +* Systemd opens the encrypted block device as per configuration in +/etc/crypttab+ + +* Creating VG and LV as needed: The service +setup-lvs+ (and corresponding + shell script) creates a volume group named +store+ in the encrypted block device + (unless it exists already) and makes sure that the logical volume + +shared-data+ exists inside this volume group + +* Systemd opens and activates all logical volumes + +* The service +setup-shared-data+ creates a filesystem in the +store/shared-data+ + logical volume (unless it contain a filesystem already). Similarly, + services +setup-shared-crypto+ and +setup-shared-backup+ creates + filesystems in the +store/shared-crypto+ and +store/shared-backup+. + +* Systemd then proceeds to fsck and mount the +shared-data+ volume as + +/var/lib/ic/data+, the +shared-crypto+ volume as +/var/lib/ic/crypto+, + and +shared-backup+ volume as +/var/lib/ic/backup+ + +All of the above actions are chained using appropriate +Before+/+RequiredBy+ +and +After+/+Requires+ dependencies expressed in the service definitions. + +=== /var filesystem setup + +Partition numbers 6 or 9 (for system A and system B, respectively) are used +for the /var filesystem hierarchy. It is set up as an encrypted filesystem +as well, but its lifetime is limited to the system that it is associated with: +If system A is upgraded to system B, then the /var partition associated of +system B is set up from scratch on first boot of system B. The (now unused) +/var partition of system A will be scrapped and overwritten on next upgrade +written into system A again. + +The partition is set up as an encrypted partition as well (since IC intermediary +data might leak to it). This is facilitated in the following way: + +* The script +/opt/ic/bin/setup-var-encryption.sh+ will check if the partition + is set up correctly already. If it is, then it is simply used as-is. + Otherwise, it is reformatted as an encrypted partition, and a filesystem + is put in. The filesystem is initialized from the filesystem state of + the /var subtree that is part of the root filesystem. (So this effectively + serves as a template defining initial structure of /var). + +* The unit file triggering this script is dynamically generated through + +/etc/systemd/system-generators/mount-generator+: The generater will + check which partition is the correct one to use and synthesize a proper + unit file. + +When an upgrade is installed into either system slot A or B, it is ensured +that the corresponding /var partition is wiped such that the newly booted +system will set up its own /var filesystem correctly again. + +=== IC bootstrap + +The +bootstrap-ic-node+ service (and its corresponding) shell script performs +customization of the installation using node-specific information. This includes: + +* network environment + +* keys or registration parameters for the IC node software + +For all of the above, the system expects a file +ic-bootstrap.tar+ - either +already present at +/mnt+ or supplied on a removable storage medium (e.g. +a USB stick or an optical medium). + +==== Network configuration + +The network configuration is performed using a file +network.conf+ in the +bootstrap tarball. It must contain lines of "key=value" statements, +with the following keys supported: + +* ipv6_address: address used for the IC replica service +* ipv6_gateway: gateway used for the primary interface +* name_servers: space-separated list of DNS servers + +This configuration file is simply copied to the +config+ partition and evaluated +on each boot to set up network. + +==== Journalbeat configuration + +The Journalbeat configuration is performed using a file +journalbeat.conf+ in +the bootstrap tarball. It must contain lines of "key=value= statements, +with the following keys supported: + +* journalbeat_hosts: space-separated list of logging hosts +* journalbeat_tags: space-separated list of tags + +== SELinux + +The system will (eventually) run SELinux in enforcing mode for security. This +requires that all system objects including all files on filesystems are +labelled appropriately. The "usual" way of setting up such a system is +to run it in "permissive" mode first on top of an (SELinux-less) base +install, however this would not work for our cases as we never want the +system to be in anything else than "enforcing" mode (similarly as for +embedded systems in general). + +Instead, SELinux is installed using docker into the target system, but +without applying any file labels (which would not be possible in docker +anyways). The labelling is then applied when extracting the docker image +into a regular filesystem image, with labels applied as per ++/etc/selinux/default/contexts/files/file_contexts+ in the file system +tree. + +Since the system has never run, some files that would have "usually" been +created do not exist yet and are not labelled -- to account for this, +a small number of additional permissions not foreseen in the reference +policy are required -- this is contained in module +fixes.te+ and set +up as part of the +prep.sh+ script called in docker. diff --git a/ic-os/setupos/rootfs/docker-base b/ic-os/setupos/rootfs/docker-base new file mode 100644 index 00000000000..d55afb653e8 --- /dev/null +++ b/ic-os/setupos/rootfs/docker-base @@ -0,0 +1 @@ +dfinity/guestos-base@sha256:a9e4fb53caee9fb749c2c1aa0ae67ce039165ed2d24bd83f6bf965704c645044 diff --git a/ic-os/setupos/rootfs/etc/default/locale b/ic-os/setupos/rootfs/etc/default/locale new file mode 100644 index 00000000000..01ec548f822 --- /dev/null +++ b/ic-os/setupos/rootfs/etc/default/locale @@ -0,0 +1 @@ +LANG=en_US.UTF-8 diff --git a/ic-os/setupos/rootfs/etc/fstab b/ic-os/setupos/rootfs/etc/fstab new file mode 100644 index 00000000000..c15986b1b32 --- /dev/null +++ b/ic-os/setupos/rootfs/etc/fstab @@ -0,0 +1,8 @@ +PARTUUID=7c0a626e-e5ea-e543-b5c5-300eb8304db7 / ext4 defaults,sync 0 1 +tmpfs /tmp tmpfs defaults 0 2 +PARTUUID=b78084e2-3363-1346-8c25-d426f26b8928 /boot/efi vfat defaults 0 2 +PARTUUID=6788e4cf-f456-104e-9a34-a2c58cfb0ee6 /boot/grub vfat defaults 0 2 +PARTUUID=a5ba3816-beaa-d74d-993e-cfa5aa6ba1f6 /config vfat defaults 0 2 +PARTUUID=ddf618fe-7244-b446-a175-3296e6b9d02e /boot ext4 defaults,sync 0 2 +PARTUUID=e97bf7b1-9400-439f-93d0-13276111f989 /data ext4 defaults,sync 0 2 +tmpfs /var tmpfs defaults 0 2 diff --git a/ic-os/setupos/rootfs/etc/hostname b/ic-os/setupos/rootfs/etc/hostname new file mode 100644 index 00000000000..d1723513861 --- /dev/null +++ b/ic-os/setupos/rootfs/etc/hostname @@ -0,0 +1 @@ +SetupOS diff --git a/ic-os/setupos/rootfs/etc/hosts b/ic-os/setupos/rootfs/etc/hosts new file mode 100644 index 00000000000..62c5dcc4fdd --- /dev/null +++ b/ic-os/setupos/rootfs/etc/hosts @@ -0,0 +1,8 @@ +127.0.0.1 localhost localhost.localdomain + +# The following lines are desirable for IPv6 capable hosts +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters diff --git a/ic-os/setupos/rootfs/etc/systemd/system-generators/systemd-gpt-auto-generator b/ic-os/setupos/rootfs/etc/systemd/system-generators/systemd-gpt-auto-generator new file mode 100755 index 00000000000..f9c586b7b89 --- /dev/null +++ b/ic-os/setupos/rootfs/etc/systemd/system-generators/systemd-gpt-auto-generator @@ -0,0 +1,6 @@ +#!/bin/bash + +# This generator normally scans gpt partition table in order to auto-mount +# filesystems based on partition labels. It does not perform anything useful +# in our case, so simply disable it. (Otherwise, needs security policy to +# allow it to do a number of things to avoid failure messages). diff --git a/ic-os/setupos/rootfs/etc/systemd/system/setupos.service b/ic-os/setupos/rootfs/etc/systemd/system/setupos.service new file mode 100644 index 00000000000..21a15ed613f --- /dev/null +++ b/ic-os/setupos/rootfs/etc/systemd/system/setupos.service @@ -0,0 +1,12 @@ +[Unit] +Description=SetupOS install process + +[Install] +WantedBy=multi-user.target + +[Service] +Type=idle +RemainAfterExit=true +ExecStart=/opt/ic/bin/setupos.sh +StandardOutput=file:/dev/tty1 +StandardError=file:/dev/tty1 diff --git a/ic-os/setupos/src/nocloud/08_devices.sh b/ic-os/setupos/rootfs/opt/ic/bin/devices.sh similarity index 59% rename from ic-os/setupos/src/nocloud/08_devices.sh rename to ic-os/setupos/rootfs/opt/ic/bin/devices.sh index 3344526b7bd..86f46ccc85e 100755 --- a/ic-os/setupos/src/nocloud/08_devices.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/devices.sh @@ -6,53 +6,42 @@ set -o pipefail SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" -function mount_src_config_partition() { - echo "* Mounting source config partition..." +function mount_config_partition() { + echo "* Mounting config partition..." - mkdir --parents /media/config_src - log_and_reboot_on_error "${?}" "Unable to create mount directory." - - # TODO: Use UUID or label instead of hard-coded path - mount /dev/sda3 /media/config_src - log_and_reboot_on_error "${?}" "Unable to mount config partition." -} - -function mount_dst_config_partition() { - echo "* Mounting destination config partition..." - - mkdir --parents /media/config_dst + mkdir --parents /media/config log_and_reboot_on_error "${?}" "Unable to create mount directory." vgchange -ay hostlvm log_and_reboot_on_error "${?}" "Unable to activate config partition." - mount /dev/mapper/hostlvm-config /media/config_dst + mount /dev/mapper/hostlvm-config /media/config log_and_reboot_on_error "${?}" "Unable to mount config partition." } function copy_config_files() { echo "* Copying config.ini to config partition..." - if [ -f "/media/config_src/config.ini" ]; then - cp /media/config_src/config.ini /media/config_dst/config.ini + if [ -f "/config/config.ini" ]; then + cp /config/config.ini /media/config/config.ini log_and_reboot_on_error "${?}" "Unable to copy config.ini to config partition." else log_and_reboot_on_error "1" "Configuration file 'config.ini' does not exist." fi echo "* Copying SSH authorized keys..." - if [ -d "/media/config_src/ssh_authorized_keys" ]; then - cp -r /media/config_src/ssh_authorized_keys /media/config_dst/ + if [ -d "/config/ssh_authorized_keys" ]; then + cp -r /config/ssh_authorized_keys /media/config/ log_and_reboot_on_error "${?}" "Unable to copy SSH authorized keys to config partition." else log_and_reboot_on_error "1" "Directory 'ssh_authorized_keys' does not exist." fi echo "* Copying deployment.json to config partition..." - cp /media/cdrom/nocloud/deployment.json /media/config_dst/deployment.json + cp /data/deployment.json /media/config/deployment.json log_and_reboot_on_error "${?}" "Unable to copy deployment.json to config partition." echo "* Copying NNS public key to config partition..." - cp /media/cdrom/nocloud/nns_public_key.pem /media/config_dst/nns_public_key.pem + cp /data/nns_public_key.pem /media/config/nns_public_key.pem log_and_reboot_on_error "${?}" "Unable to copy NNS public key to config partition." } @@ -76,11 +65,8 @@ function unmount_config_partition() { sync log_and_reboot_on_error "${?}" "Unable to synchronize cached writes to persistent storage." - umount /media/config_src - log_and_reboot_on_error "${?}" "Unable to unmount source config partition." - - umount /media/config_dst - log_and_reboot_on_error "${?}" "Unable to unmount destination config partition." + umount /media/config + log_and_reboot_on_error "${?}" "Unable to unmount config partition." vgchange -an hostlvm log_and_reboot_on_error "${?}" "Unable to deactivate config partition." @@ -88,14 +74,13 @@ function unmount_config_partition() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start - mount_src_config_partition - mount_dst_config_partition + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" + mount_config_partition copy_config_files insert_hsm unmount_config_partition - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/src/nocloud/05_disk.sh b/ic-os/setupos/rootfs/opt/ic/bin/disk.sh similarity index 93% rename from ic-os/setupos/src/nocloud/05_disk.sh rename to ic-os/setupos/rootfs/opt/ic/bin/disk.sh index a18fb5c7986..22a0ff6c068 100755 --- a/ic-os/setupos/src/nocloud/05_disk.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/disk.sh @@ -43,11 +43,11 @@ function purge_partitions() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" purge_volume_groups purge_partitions - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/src/nocloud/03_firmware.sh b/ic-os/setupos/rootfs/opt/ic/bin/firmware.sh similarity index 90% rename from ic-os/setupos/src/nocloud/03_firmware.sh rename to ic-os/setupos/rootfs/opt/ic/bin/firmware.sh index 0ee290e5f3e..8f7fce947b5 100755 --- a/ic-os/setupos/src/nocloud/03_firmware.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/firmware.sh @@ -43,14 +43,14 @@ function set_nic() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" set_uefi set_idrac set_chipset set_psu set_nic - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/src/nocloud/00_common.sh b/ic-os/setupos/rootfs/opt/ic/bin/functions.sh similarity index 62% rename from ic-os/setupos/src/nocloud/00_common.sh rename to ic-os/setupos/rootfs/opt/ic/bin/functions.sh index 96dfedd4fe9..2b3699a303b 100755 --- a/ic-os/setupos/src/nocloud/00_common.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/functions.sh @@ -11,12 +11,48 @@ function log_and_reboot_on_error() { local log_message="${2}" if [ "${exit_code}" -ne 0 ]; then + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " echo " " echo " " echo " " echo " " echo "--------------------------------------------------------------------------------" - echo " INTERNET COMPUTER SETUP FAILED" + echo " INTERNET COMPUTER - SETUP - FAILED" echo "--------------------------------------------------------------------------------" echo " " echo " " @@ -44,18 +80,22 @@ function log_and_reboot_on_error() { } function log_start() { + local script="${1}" TIME_START=$(date '+%s') - echo "SetupOS - Start" - log_and_reboot_on_error "${?}" "Unable to start SetupOS script." + echo "${script} - Start" + log_and_reboot_on_error "${?}" "Unable to start '${script}' script." + echo " " } function log_end() { + local script="${1}" local time_end=$(date '+%s') local time_exec=$(expr "${time_end}" - "${TIME_START}") local time_hr=$(date -d "1970-01-01 ${time_exec} sec" '+%H:%M:%S') - echo "SetupOS - End (${time_hr})" - log_and_reboot_on_error "${?}" "Unable to end SetupOS script." + echo " " + echo "${script} - End (${time_hr})" + log_and_reboot_on_error "${?}" "Unable to end '${script}' script." echo " " } diff --git a/ic-os/setupos/src/nocloud/07_guestos.sh b/ic-os/setupos/rootfs/opt/ic/bin/guestos.sh similarity index 81% rename from ic-os/setupos/src/nocloud/07_guestos.sh rename to ic-os/setupos/rootfs/opt/ic/bin/guestos.sh index 27175f6cf26..40b19900aa2 100755 --- a/ic-os/setupos/src/nocloud/07_guestos.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/guestos.sh @@ -14,7 +14,7 @@ function install_guestos() { vgchange -ay hostlvm log_and_reboot_on_error "${?}" "Unable to activate HostOS volume group." - tar xzOf /media/cdrom/nocloud/guest-os.img.tar.gz disk.img | dd of=${LV} bs=10M + tar xzOf /data/guest-os.img.tar.gz disk.img | dd of=${LV} bs=10M log_and_reboot_on_error "${?}" "Unable to install GuestOS disk-image." sync @@ -26,10 +26,10 @@ function install_guestos() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" install_guestos - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/src/nocloud/02_hardware.sh b/ic-os/setupos/rootfs/opt/ic/bin/hardware.sh similarity index 97% rename from ic-os/setupos/src/nocloud/02_hardware.sh rename to ic-os/setupos/rootfs/opt/ic/bin/hardware.sh index a6edaa84478..a33218fa62f 100755 --- a/ic-os/setupos/src/nocloud/02_hardware.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/hardware.sh @@ -107,12 +107,12 @@ function verify_disk() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" verify_cpu verify_disk verify_memory - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/src/nocloud/06_hostos.sh b/ic-os/setupos/rootfs/opt/ic/bin/hostos.sh similarity index 92% rename from ic-os/setupos/src/nocloud/06_hostos.sh rename to ic-os/setupos/rootfs/opt/ic/bin/hostos.sh index f12002082bf..b615a821631 100755 --- a/ic-os/setupos/src/nocloud/06_hostos.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/hostos.sh @@ -8,7 +8,7 @@ PATH="/sbin:/bin:/usr/sbin:/usr/bin" function install_hostos() { echo "* Installing HostOS disk-image (can take up to 5 minutes)..." - tar xzOf /media/cdrom/nocloud/host-os.img.tar.gz disk.img | dd of="/dev/nvme0n1" bs=10M + tar xzOf /data/host-os.img.tar.gz disk.img | dd of="/dev/nvme0n1" bs=10M log_and_reboot_on_error "${?}" "Unable to install HostOS disk-image on drive: /dev/nvme0n1" sync @@ -61,12 +61,12 @@ function resize_partition() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh - log_start + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" install_hostos configure_efi resize_partition - log_end + log_end "$(basename $0)" } main diff --git a/ic-os/setupos/rootfs/opt/ic/bin/setupos.sh b/ic-os/setupos/rootfs/opt/ic/bin/setupos.sh new file mode 100755 index 00000000000..684792cac02 --- /dev/null +++ b/ic-os/setupos/rootfs/opt/ic/bin/setupos.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +set -o nounset +set -o pipefail + +SHELL="/bin/bash" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" + +function start_setupos() { + echo "* Starting SetupOS..." + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo "-------------------------------------------------------------------------------" + echo " INTERNET COMPUTER - SETUP" + echo "-------------------------------------------------------------------------------" + echo " " + echo " " +} + +function reboot_setupos() { + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " " + echo " Please do NOT unplug the Nitrokey HSM USB device just yet." + echo " " + echo " Wait for the message after the first boot..." + echo " " + echo " " + echo " " + echo " " + echo "-------------------------------------------------------------------------------" + echo " INTERNET COMPUTER - SETUP - SUCCESSFUL" + echo "-------------------------------------------------------------------------------" + echo " " + echo " " + echo " " + echo " " + echo "* Rebooting SetupOS..." + echo " " + sleep 15 + shutdown -r now +} + +# Establish run order +main() { + source /opt/ic/bin/functions.sh + log_start "$(basename $0)" + # Wait until login prompt appears + sleep 10 + start_setupos + /opt/ic/bin/hardware.sh + # NOTE: Firmware up-/downgrades are currently applied manually + #/opt/ic/bin/firmware.sh + # NOTE: UEFI settings are currently applied manually + #/opt/ic/bin/uefi.sh + /opt/ic/bin/disk.sh + /opt/ic/bin/hostos.sh + /opt/ic/bin/guestos.sh + /opt/ic/bin/devices.sh + reboot_setupos + log_end "$(basename $0)" +} + +main diff --git a/ic-os/setupos/src/nocloud/04_uefi.sh b/ic-os/setupos/rootfs/opt/ic/bin/uefi.sh similarity index 92% rename from ic-os/setupos/src/nocloud/04_uefi.sh rename to ic-os/setupos/rootfs/opt/ic/bin/uefi.sh index 4f624c9f92b..f68b45920e2 100755 --- a/ic-os/setupos/src/nocloud/04_uefi.sh +++ b/ic-os/setupos/rootfs/opt/ic/bin/uefi.sh @@ -22,7 +22,7 @@ function verify_uefi_config() { # Establish run order main() { - source /media/cdrom/nocloud/00_common.sh + source /opt/ic/bin/functions.sh log_start apply_uefi_config verify_uefi_config diff --git a/ic-os/setupos/rootfs/packages.common b/ic-os/setupos/rootfs/packages.common new file mode 100644 index 00000000000..e4e0b345bb9 --- /dev/null +++ b/ic-os/setupos/rootfs/packages.common @@ -0,0 +1,44 @@ +# This defines all Ubuntu packages to be installed on the target image. The +# packages here are intended to be the bare minimum required for +# operation for the "prod" image. + +# Need kernel to boot anything +linux-image-virtual-hwe-20.04 +initramfs-tools + +# Need systemd for boot process +systemd +systemd-sysv +systemd-journal-remote + +# Required system setup tools XXX TODO +attr +ca-certificates +curl +efibootmgr +faketime +fdisk +gdisk +iproute2 +jq +less +lshw +lvm2 +net-tools +parted +sudo +udev +usbutils +xfsprogs + +# SELinux support +selinux-policy-default +selinux-utils +semodule-utils +policycoreutils +# this is required for policy building -- presently policy modules are built +# inside the target which is not fully proper. When the build is moved out, +# this package can be removed +selinux-policy-dev +checkpolicy + diff --git a/ic-os/setupos/rootfs/packages.dev b/ic-os/setupos/rootfs/packages.dev new file mode 100644 index 00000000000..f6019834d86 --- /dev/null +++ b/ic-os/setupos/rootfs/packages.dev @@ -0,0 +1,24 @@ +# These are additional Ubuntu packages installed on the "dev" images. + +# editor for convenience +vim + +# packages requested by networking +tcpdump +iperf +netcat +curl +iputils-ping + +# Third-party services we will be running +openssh-server + +# may want to ssh into other hosts -- not sure if this is really advisable +openssh-client + +# useful for first-order debugging +strace + +# useful for SELinux development +setools + diff --git a/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.fc b/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.fc new file mode 100644 index 00000000000..64550ca99dd --- /dev/null +++ b/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.fc @@ -0,0 +1,9 @@ +/lib -l system_u:object_r:lib_t:s0 +/lib32 -l system_u:object_r:lib_t:s0 +/lib64 -l system_u:object_r:lib_t:s0 +/usr/lib32 -d system_u:object_r:lib_t:s0 +/usr/libx32 -d system_u:object_r:lib_t:s0 +/bin -l system_u:object_r:bin_t:s0 +/sbin -l system_u:object_r:bin_t:s0 +/root -d system_u:object_r:user_home_dir_t:s0 +/home -d system_u:object_r:home_root_t:s0 diff --git a/ic-os/setupos/src/nocloud/meta-data b/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.if similarity index 100% rename from ic-os/setupos/src/nocloud/meta-data rename to ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.if diff --git a/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.te b/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.te new file mode 100644 index 00000000000..f04e1567e33 --- /dev/null +++ b/ic-os/setupos/rootfs/prep/fscontext-fixes/fscontext-fixes.te @@ -0,0 +1,19 @@ +policy_module(fscontext-fixes, 1.0.0) + +# This policy module contains fixes for incorrect file contexts that are required +# due to some discrepancies in which the reference policy is used: +# +# - building using "make_ext4fs" instead of "restorecon + mkfs.ext4": file contexts of certain +# symbolic links and library directories are wrong because make_ext4fs lacks some inference rules +# +# - building a system that may not have all necessary files and not running autorelabel: +# certain files need to be created at run time + +# Under normal circumstances, this socket file is labelled by autorelabel. +# We are not running it, so simply allow syslog to create the socket. + +require { + type syslogd_t, syslogd_var_run_t; +} + +allow syslogd_t syslogd_var_run_t : sock_file { create }; diff --git a/ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.if b/ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.if new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.te b/ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.te new file mode 100644 index 00000000000..2c2e7ba4147 --- /dev/null +++ b/ic-os/setupos/rootfs/prep/misc-fixes/misc-fixes.te @@ -0,0 +1,31 @@ +policy_module(misc-fixes, 1.0.0) + +# Miscellaneous small fixes for policy (everything not related to systemd +# policy) + +############################################################################### +# Mark unconfined_t as permissive for now, until policy development is +# completed. +require { type unconfined_t; } +permissive unconfined_t; + +# mount + +# We are dealing with vfat filesystem for the configuration injection. +require { type mount_t, dosfs_t; } +allow mount_t dosfs_t : filesystem { getattr }; +# Allow basic permissions around umounting +require { type initrc_tmp_t; } +allow mount_t initrc_tmp_t : file { read }; + +############################################################################### +# partprobe + +require { type fsadm_t, dmidecode_exec_t, kernel_t, udev_exec_t; } + +# It wants to do something dmidecode -- no good reason, however. Seems to work without. +dontaudit fsadm_t dmidecode_exec_t : file { execute }; +# Also wants to retrieve IPC info from kernel. No good reason, and works without. +dontaudit fsadm_t kernel_t : system { ipc_info }; +# Also wants udevadm. Again, no good reason, and works without. +dontaudit fsadm_t udev_exec_t : file { execute }; diff --git a/ic-os/setupos/rootfs/prep/prep.sh b/ic-os/setupos/rootfs/prep/prep.sh new file mode 100755 index 00000000000..52372c83934 --- /dev/null +++ b/ic-os/setupos/rootfs/prep/prep.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +# Policy building feeds wildcard-generated list of files to m4. Unfortunately, +# implicit sorting of all wildcard globs was added in make version 4.3, and we use 4.2. +# Simply patch the Makefile to manually force sorting. +sed -e 's/m4support = \(.*\)/m4support = $(sort \1)/' -i /usr/share/selinux/devel/include/Makefile +sed -e 's/header_interfaces := \(.*\)/header_interfaces := $(sort \1)/' -i /usr/share/selinux/devel/include/Makefile +sed -e 's/detected_mods := \(.*\)/detected_mods := $(sort \1)/' -i /usr/share/selinux/devel/include/Makefile + +# Build SELinux modules +make -f /usr/share/selinux/devel/Makefile + +# Force unsharing of directory -- seems to be a docker bug +mv /var/lib/selinux/default/active /var/lib/selinux/default/active.unshare +mv /var/lib/selinux/default/active.unshare /var/lib/selinux/default/active + +# Install them +semodule -i *.pp diff --git a/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.if b/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.if new file mode 100644 index 00000000000..fc325e919e8 --- /dev/null +++ b/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.if @@ -0,0 +1,41 @@ +# This macro allows various domains to read efivars. This is required by various systemd services +# and contained in later reference policies, but missing from the policy shipped by Ubuntu. + +interface(`require_read_efivarfs_files',` + gen_require(` + type $1; + ') + + fs_read_efivarfs_files($1) +') + +# Allow to read into /run/systemd: These are generated configuration files and similar which various +# services need to access. +interface(`require_read_run_systemd',` + gen_require(` + type $1, init_var_run_t; + ') + read_files_pattern($1, init_var_run_t, init_var_run_t) + list_dirs_pattern($1, init_var_run_t, init_var_run_t) +') + + +# Allow to read into /proc/sys/kernel. +interface(`require_read_proc_sys_kernel',` + gen_require(` + type $1, sysctl_t, sysctl_kernel_t; + ') + read_files_pattern($1, {sysctl_t sysctl_kernel_t}, {sysctl_t sysctl_kernel_t}) +') + +# Allow to connect to /run/systemd/userdb/io.systemd.DynamicUser +# and read systemd userdb +interface(`systemd_connect_userdb',` + gen_require(` + type $1, init_var_run_t; + ') + list_dirs_pattern($1, init_var_run_t, init_var_run_t) + read_files_pattern($1, init_var_run_t, init_var_run_t) + init_stream_connect($1) + init_write_runtime_socket($1) +') diff --git a/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.te b/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.te new file mode 100644 index 00000000000..51be045f70d --- /dev/null +++ b/ic-os/setupos/rootfs/prep/systemd-fixes/systemd-fixes.te @@ -0,0 +1,126 @@ +policy_module(systemd-fixes, 1.0.0) + +############################################################################### +# systemd-journal + +# systemd allows service units to use NoNewPrivileges. While this is not +# necessarily sensible under selinux (which enforces much broader behavior +# bounds), allowing this silences warnings. Maybe a dontaudit rule would +# also be adequate, but it does not hurt allowing it. +require { type init_t, initrc_t; } +allow init_t initrc_t : process2 { nnp_transition }; + +############################################################################### +# systemd-journal + +# not clear why, but harmless +require_read_efivarfs_files(syslogd_t) +# read /run/systemd for dynamic configuration +require_read_run_systemd(syslogd_t) + +# XXX: not clear +allow syslogd_t syslogd_t : netlink_generic_socket { create ioctl }; + +# journald wants to scan the /run/user hierarchy (presumably relating to login sessions) +require { type user_runtime_root_t, user_runtime_t; } +allow syslogd_t user_runtime_root_t : dir { search }; +allow syslogd_t user_runtime_t : dir { search }; +# it wants to override dac read permissions for the /run/user hierarchy +allow syslogd_t syslogd_t : capability { dac_read_search }; + +############################################################################### +# systemd-networkd + +# not clear why, but harmless +require_read_efivarfs_files(systemd_networkd_t) +# read dynamically generated network config files +require_read_run_systemd(systemd_networkd_t) +# communicate completion & state to other init scripts +init_dbus_send_script(systemd_networkd_t) +# netlink socket to actually monitor network interfaces +allow systemd_networkd_t systemd_networkd_t : netlink_generic_socket { bind create getattr getopt setopt }; +# allow networkd to operate dhcp client socket +corenet_udp_bind_dhcpc_port(systemd_networkd_t) +corenet_udp_bind_generic_node(systemd_networkd_t) +# allow self-signal +allow systemd_networkd_t systemd_networkd_t : process { signal }; + +############################################################################### +# systemd-detect-virt + +# This wants to probe various places of the system for information whether we are +# running under virtualization, and what kind of virtualization. + +# not clear why, but harmless +require_read_efivarfs_files(systemd_detect_virt_t) +# reads /proc/1/environ to determine things from the init process +read_files_pattern(systemd_detect_virt_t, init_t, init_t) +# reads /run/systemd for static information +require_read_run_systemd(systemd_detect_virt_t) +# reads /proc/cmdline +require { type proc_t; } +read_files_pattern(systemd_detect_virt_t, proc_t, proc_t) +# reads /proc/sys/kernel/osrelease +require_read_proc_sys_kernel(systemd_detect_virt_t) + +############################################################################### +# systemd-resolved + +# not clear why, but harmless +require_read_efivarfs_files(systemd_resolved_t) +# it wants to read certificates +miscfiles_read_generic_certs(systemd_resolved_t) + +############################################################################### +# systemd-user-sessions + +# not clear why, but harmless +require_read_efivarfs_files(systemd_sessions_t) +# reads /proc/sys/kernel/osrelease +require_read_proc_sys_kernel(systemd_sessions_t) + +############################################################################### +# systemd-tmpfiles + +# not clear why, but harmless +require_read_efivarfs_files(systemd_tmpfiles_t) + +# read /run/systemd for dynamic configuration +require_read_run_systemd(systemd_tmpfiles_t) + +# manage symlink in (temporary) dbus directory +require { type system_dbusd_var_lib_t; } +manage_lnk_files_pattern(systemd_tmpfiles_t, system_dbusd_var_lib_t, system_dbusd_var_lib_t) + +# interact with early lvm +require { type lvm_lock_t; } +list_dirs_pattern(systemd_tmpfiles_t, lvm_lock_t, lvm_lock_t) + +# associate correct labels to early unlabeled files +require { type tmpfs_t, unlabeled_t; } +relabelfrom_dirs_pattern(systemd_tmpfiles_t, { tmpfs_t unlabeled_t }, { tmpfs_t unlabeled_t }) +list_dirs_pattern(systemd_tmpfiles_t, unlabeled_t, unlabeled_t) + +############################################################################### +# udev + +# not clear why, but harmless +require_read_efivarfs_files(udev_t) + +# read /run/systemd for dynamic configuration +require_read_run_systemd(udev_t) + +############################################################################### +# systemd-logind & userdb + +require { type systemd_logind_t; } + +systemd_connect_userdb(systemd_logind_t) + +# tries to read /etc/shadow, but should not and does not need to: deny, but +# suppress audit message to avoid spamming logs +# See https://github.com/systemd/systemd/issues/15105 +auth_dontaudit_read_shadow(systemd_logind_t) + +# local login parts +systemd_connect_userdb(local_login_t) diff --git a/ic-os/setupos/scripts/artifact-utils.sh b/ic-os/setupos/scripts/artifact-utils.sh new file mode 100755 index 00000000000..9020aa638ac --- /dev/null +++ b/ic-os/setupos/scripts/artifact-utils.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +source $(dirname "${BASH_SOURCE[0]}")/partitions.sh + +# Extracts the version number from built disk image +# Arguments: +# - $1: disk image file +# +# Output: the version number +function version_from_disk_image() { + local DISK_IMAGE=$1 + local PART_IMAGE=$(mktemp) + extract_single_partition "${DISK_IMAGE}" boot "${PART_IMAGE}" 2>/dev/null + VERSION=$(debugfs "${PART_IMAGE}" -R "cat version.txt" 2>/dev/null || echo -n "unknown") + #debugfs "${PART_IMAGE}" -R ls + rm "${PART_IMAGE}" + echo "${VERSION}" +} diff --git a/ic-os/setupos/scripts/build-disk-image.sh b/ic-os/setupos/scripts/build-disk-image.sh new file mode 100755 index 00000000000..4fb64883749 --- /dev/null +++ b/ic-os/setupos/scripts/build-disk-image.sh @@ -0,0 +1,321 @@ +#!/usr/bin/env bash + +# Build bootable SetupOS disk image + +set -o errexit +set -o pipefail +# NOTE: Validating inputs manually +#set -o nounset + +SHELL="/bin/bash" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" + +BASE_DIR="$(dirname "${BASH_SOURCE[0]}")/.." +TMP_DIR="$(mktemp -d)" +echo $TMP_DIR +trap "rm -rf ${TMP_DIR}" EXIT +TOOL_DIR="${BASE_DIR}/../../toolchains/sysimage/" +BASE_IMAGE=$(cat "${BASE_DIR}/rootfs/docker-base") + +# Fixed timestamp for reproducible build +TOUCH_TIMESTAMP="200901031815.05" +export SOURCE_DATE_EPOCH="1231006505" + +# Get keyword arguments +for argument in "${@}"; do + case ${argument} in + -d=* | --deployment=*) + DEPLOYMENT="${argument#*=}" + shift + ;; + -h | --help) + echo 'Usage: +SetupOS Builder + +Arguments: + -d=, --deployment= specify the deployment name (Default: mainnet) + --host-os= specify the HostOS disk-image file (Default: ./host-os.img.tar.gz) + --guest-os= specify the GuestOS disk-image file (Default: ./guest-os.img.tar.gz) + -h, --help show this help message and exit + -k=, --key= specify the NNS public key (Example: ./nns_public_key.pem) + -l=, --logging= specify the logging hosts/destination (Default: telemetry01.mainnet.dfinity.network telemetry02.mainnet.dfinity.network telemetry03.mainnet.dfinity.network) + -n=, --nns-url= specify the NNS URL for the GuestOS (Default: ) + -o=, --output= ISO output directory (Default: ./) + -s=, --name-servers= specify the DNS name servers (Default: 2606:4700:4700::1111 2606:4700:4700::1001 2001:4860:4860::8888 2001:4860:4860::8844) + --memory= specify the amount of memory in GiB (Gibibytes) for the GuestOS (Default: 490) + -v=, --version specify the SetupOS version +' + exit 1 + ;; + --host-os=*) + HOST_OS="${argument#*=}" + shift + ;; + --guest-os=*) + GUEST_OS="${argument#*=}" + shift + ;; + -k=* | --key=*) + KEY="${argument#*=}" + shift + ;; + -l=* | --logging=*) + LOGGING="${argument#*=}" + shift + ;; + -n=* | --nns-url=*) + NNS_URL="${argument#*=}" + shift + ;; + -o=* | --output=*) + OUTPUT="${argument#*=}" + shift + ;; + -s=* | --name-servers=*) + NAME_SERVERS="${argument#*=}" + shift + ;; + --memory=*) + MEMORY="${argument#*=}" + shift + ;; + -v=* | --version=*) + VERSION="${argument#*=}" + shift + ;; + *) + echo "Error: Argument is not supported: ${argument#*=}" >&2 + exit 1 + ;; + esac +done + +# Set arguments if undefined +DEPLOYMENT="${DEPLOYMENT:=mainnet}" +HOST_OS="${HOST_OS:=${BASE_DIR}/host-os.img.tar.gz}" +GUEST_OS="${GUEST_OS:=${BASE_DIR}/guest-os.img.tar.gz}" +LOGGING="${LOGGING:=elasticsearch-node-0.mercury.dfinity.systems:443 elasticsearch-node-1.mercury.dfinity.systems:443 elasticsearch-node-2.mercury.dfinity.systems:443 elasticsearch-node-3.mercury.dfinity.systems:443}" +MEMORY="${MEMORY:=490}" +NNS_URL="${NNS_URL:=http://[2600:c02:b002:15:5000:ceff:fecc:d5cd]:8080,http://[2604:3fc0:3002:0:5000:6bff:feb9:6baf]:8080,http://[2a00:fb01:400:100:5000:5bff:fe6b:75c6]:8080,http://[2604:3fc0:2001:0:5000:b0ff:fe7b:ff55]:8080,http://[2600:3000:6100:200:5000:cbff:fe4b:b207]:8080,http://[2604:3fc0:3002:0:5000:4eff:fec2:4806]:8080,http://[2001:920:401a:1708:5000:5fff:fec1:9ddb]:8080,http://[2001:920:401a:1706:5000:87ff:fe11:a9a0]:8080,http://[2401:3f00:1000:24:5000:deff:fed6:1d7]:8080,http://[2a00:fb01:400:100:5000:61ff:fe2c:14ac]:8080,http://[2a04:9dc0:0:108:5000:ccff:feb7:c03b]:8080,http://[2600:c02:b002:15:5000:53ff:fef7:d3c0]:8080,http://[2401:3f00:1000:22:5000:c3ff:fe44:36f4]:8080,http://[2607:f1d0:10:1:5000:a7ff:fe91:44e]:8080,http://[2a04:9dc0:0:108:5000:96ff:fe4a:be10]:8080,http://[2604:7e00:50:0:5000:20ff:fea7:efee]:8080,http://[2600:3004:1200:1200:5000:59ff:fe54:4c4b]:8080,http://[2a0f:cd00:2:1:5000:3fff:fe36:cab8]:8080,http://[2401:3f00:1000:23:5000:80ff:fe84:91ad]:8080,http://[2607:f758:c300:0:5000:72ff:fe35:3797]:8080,http://[2607:f758:1220:0:5000:12ff:fe0c:8a57]:8080,http://[2a01:138:900a:0:5000:2aff:fef4:c47e]:8080,http://[2a0f:cd00:2:1:5000:87ff:fe58:ceba]:8080,http://[2401:3f00:1000:24:5000:86ff:fea6:9bb5]:8080,http://[2600:2c01:21:0:5000:27ff:fe23:4839]:8080,http://[2a04:9dc0:0:108:5000:7cff:fece:97d]:8080,http://[2001:920:401a:1708:5000:4fff:fe92:48f1]:8080,http://[2604:3fc0:3002:0:5000:acff:fe31:12e8]:8080,http://[2a04:9dc0:0:108:5000:6bff:fe08:5f57]:8080,http://[2607:f758:c300:0:5000:3eff:fe6d:af08]:8080,http://[2607:f758:1220:0:5000:bfff:feb9:6794]:8080,http://[2607:f758:c300:0:5000:8eff:fe8b:d68]:8080,http://[2607:f758:1220:0:5000:3aff:fe16:7aec]:8080,http://[2a00:fb01:400:100:5000:ceff:fea2:bb0]:8080,http://[2a00:fa0:3:0:5000:5aff:fe89:b5fc]:8080,http://[2a00:fa0:3:0:5000:68ff:fece:922e]:8080,http://[2600:3000:6100:200:5000:c4ff:fe43:3d8a]:8080,http://[2001:920:401a:1710:5000:d7ff:fe6f:fde7]:8080,http://[2a01:138:900a:0:5000:5aff:fece:cf05]:8080,http://[2600:3006:1400:1500:5000:20ff:fe3f:3c98]:8080}" +NAME_SERVERS="${NAME_SERVERS:=2606:4700:4700::1111 2606:4700:4700::1001 2001:4860:4860::8888 2001:4860:4860::8844}" + +function validate_input() { + local variable="${1}" + local message="${2}" + + if [ -z ${variable} ]; then + echo "Missing Argument:" >&2 + echo " ${message}" >&2 + exit 1 + fi +} + +function log_and_exit_on_error() { + local exit_code="${1}" + local log_message="${2}" + + if [ "${exit_code}" -ne 0 ]; then + echo "${log_message}" >&2 + exit "${exit_code}" + fi +} + +function log_start() { + TIME_START=$(date '+%s') + + echo "SetupOS Builder - Start" + log_and_exit_on_error "${?}" "Unable to start SetupOS builder." +} + +function validate_guest_os() { + echo "* Validating GuestOS disk-image..." + if [ ! -r "${GUEST_OS}" ]; then + log_and_exit_on_error "1" "Unable to find or read GuestOS disk-image." + fi +} + +function validate_host_os() { + echo "* Validating HostOS disk-image..." + if [ ! -r "${HOST_OS}" ]; then + log_and_exit_on_error "1" "Unable to find or read HostOS disk-image." + fi +} + +function prepare_config_partition() { + echo "* Preparing config partition..." + + CONFIG_TMP="${TMP_DIR}/config" + mkdir -p ${CONFIG_TMP} + + ( + cat <"${CONFIG_TMP}/config.ini" + + mkdir -p ${CONFIG_TMP}/ssh_authorized_keys + + for file in admin backup readonly; do + ( + cat <"${CONFIG_TMP}/ssh_authorized_keys/${file}" + done + + # Fix timestamps for reproducible build + touch -t ${TOUCH_TIMESTAMP} \ + ${CONFIG_TMP}/config.ini \ + ${CONFIG_TMP}/ssh_authorized_keys \ + ${CONFIG_TMP}/ssh_authorized_keys/{admin,backup,readonly} + + #tar -cvf "${TMP_DIR}/config.tar" -C "${TMP_DIR}/" config/ + cd ${TMP_DIR} + tar -cvf "${TMP_DIR}/config.tar" -C "${TMP_DIR}/" config/ + cd - +} + +function prepare_data_partition() { + echo "* Preparing data partition..." + + DATA_TMP="${TMP_DIR}/data" + mkdir -p ${DATA_TMP} + + ( + cat <"${DATA_TMP}/deployment.json" + + if [ ! -z "${KEY}" ]; then + cat ${KEY} >"${DATA_TMP}/nns_public_key.pem" + else + ( + cat <"${DATA_TMP}/nns_public_key.pem" + fi + + # Inject deployment configuration + sed -i "s@{{ nns_url }}@${NNS_URL}@g" "${DATA_TMP}/deployment.json" + sed -i "s@{{ deployment_name }}@${DEPLOYMENT}@g" "${DATA_TMP}/deployment.json" + sed -i "s@{{ logging_hosts }}@${LOGGING}@g" "${DATA_TMP}/deployment.json" + sed -i "s@{{ dns_name_servers }}@${NAME_SERVERS}@g" "${DATA_TMP}/deployment.json" + sed -i "s@{{ resources_memory }}@${MEMORY}@g" "${DATA_TMP}/deployment.json" + + # Copy disk-image files + cp --preserve=timestamp "${GUEST_OS}" "${DATA_TMP}/guest-os.img.tar.gz" + cp --preserve=timestamp "${HOST_OS}" "${DATA_TMP}/host-os.img.tar.gz" + + # Fix timestamps for reproducible build + touch -t ${TOUCH_TIMESTAMP} \ + ${DATA_TMP}/deployment.json \ + ${DATA_TMP}/nns_public_key.pem \ + ${DATA_TMP}/guest-os.img.tar.gz \ + ${DATA_TMP}/host-os.img.tar.gz + + #tar -cvf "${TMP_DIR}/data.tar" -C "${TMP_DIR}/" data/ + cd ${TMP_DIR} + tar -cvf "${TMP_DIR}/data.tar" -C "${TMP_DIR}/" data/ + cd - +} + +function assemble_and_populate_image() { + echo "${VERSION}" >"${TMP_DIR}/version.txt" + touch -t ${TOUCH_TIMESTAMP} ${TMP_DIR}/version.txt + + # XXX Temporarily skip pull until image has been pushed + "${TOOL_DIR}"/docker_tar.py -o "${TMP_DIR}/boot-tree.tar" --skip-pull "${BASE_DIR}/bootloader" + "${TOOL_DIR}"/docker_tar.py -o "${TMP_DIR}/rootfs-tree.tar" --skip-pull -- --build-arg BASE_IMAGE="${BASE_IMAGE}" "${BASE_DIR}/rootfs" + "${TOOL_DIR}"/build_vfat_image.py -o "${TMP_DIR}/partition-esp.tar" -s 100M -p boot/efi -i "${TMP_DIR}/boot-tree.tar" + "${TOOL_DIR}"/build_vfat_image.py -o "${TMP_DIR}/partition-grub.tar" -s 100M -p boot/grub -i "${TMP_DIR}/boot-tree.tar" \ + "${BASE_DIR}/bootloader/grub.cfg:/boot/grub/grub.cfg:644" \ + "${BASE_DIR}/bootloader/grubenv:/boot/grub/grubenv:644" + + "${TOOL_DIR}"/build_fat32_image.py -o "${TMP_DIR}/partition-config.tar" -s 100M -p config/ -l CONFIG -i "${TMP_DIR}/config.tar" + "${TOOL_DIR}"/build_ext4_image.py -o "${TMP_DIR}/partition-data.tar" -s 2G -p data/ -i "${TMP_DIR}/data.tar" + + tar xOf "${TMP_DIR}"/rootfs-tree.tar --occurrence=1 etc/selinux/default/contexts/files/file_contexts >"${TMP_DIR}/file_contexts" + + "${TOOL_DIR}"/build_ext4_image.py -o "${TMP_DIR}/partition-boot.tar" -s 500M -i "${TMP_DIR}/rootfs-tree.tar" -S "${TMP_DIR}/file_contexts" -p boot/ \ + "${TMP_DIR}/version.txt:/boot/version.txt:0644" \ + "${BASE_DIR}/extra_boot_args:/boot/extra_boot_args:0644" + + "${TOOL_DIR}"/build_ext4_image.py -o "${TMP_DIR}/partition-root.tar" -s 1G -i "${TMP_DIR}/rootfs-tree.tar" -S "${TMP_DIR}/file_contexts" \ + "${TMP_DIR}/version.txt:/opt/ic/share/version.txt:0644" + + "${TOOL_DIR}"/build_disk_image.py -o "${TMP_DIR}/disk.img.tar" -p "${BASE_DIR}/scripts/partitions.csv" \ + ${TMP_DIR}/partition-esp.tar \ + ${TMP_DIR}/partition-grub.tar \ + ${TMP_DIR}/partition-config.tar \ + ${TMP_DIR}/partition-data.tar \ + ${TMP_DIR}/partition-boot.tar \ + ${TMP_DIR}/partition-root.tar +} + +function provide_raw_image() { + # For compatibility with previous use of this script, provide the raw + # image as output from this program. + OUT_DIRNAME="$(dirname "${OUTPUT}")" + OUT_BASENAME="$(basename "${OUTPUT}")" + tar xf "${TMP_DIR}/disk.img.tar" --transform="s/disk.img/${OUT_BASENAME}/" -C "${OUT_DIRNAME}" + # increase size a bit, for immediate qemu use (legacy) + truncate --size 4G "${OUTPUT}" +} + +function log_end() { + local time_end=$(date '+%s') + local time_exec=$(expr "${time_end}" - "${TIME_START}") + local time_hr=$(date -d "1970-01-01 ${time_exec} sec" '+%H:%M:%S') + + echo "SetupOS Builder - End (${time_hr})" + log_and_exit_on_error "${?}" "Unable to end SetupOS builder." +} + +# Establish run order +function main() { + log_start + validate_input "${OUTPUT}" "Output file needs to be specified for build to succeed." + validate_input "${VERSION}" "Version needs to be specified for build to succeed." + validate_guest_os + validate_host_os + prepare_config_partition + prepare_data_partition + assemble_and_populate_image + provide_raw_image + log_end +} + +main diff --git a/ic-os/setupos/scripts/build-iso.sh b/ic-os/setupos/scripts/build-iso.sh deleted file mode 100755 index c0199a5b35e..00000000000 --- a/ic-os/setupos/scripts/build-iso.sh +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env bash - -# Build Ubuntu Server based ISO image containing setup files - -# Build Requirements: -# - Operating System: Ubuntu 20.04 -# - Packages: ca-certificates, curl, git, isolinux, mtools, p7zip-full, syslinux, xorriso -# - Connectivity: 443/tcp outbound - -set -o errexit -set -o pipefail -# NOTE: Validating inputs manually -#set -o nounset - -SHELL="/bin/bash" -PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -BASE_DIR="$(dirname "${BASH_SOURCE[0]}")/.." -TMP_DIR="$(mktemp -d)" -ISO_DIR="${TMP_DIR}/iso" - -UBUNTU_VERSION="20.04.4" -UBUNTU_ISO="ubuntu-${UBUNTU_VERSION}-live-server-amd64.iso" -UBUNTU_URL="https://releases.ubuntu.com/${UBUNTU_VERSION}/${UBUNTU_ISO}" -UBUNTU_CHECKSUM="28ccdb56450e643bad03bb7bcf7507ce3d8d90e8bf09e38f6bd9ac298a98eaad" - -EFIBOOTMGR_URL="http://archive.ubuntu.com/ubuntu/pool/main/e/efibootmgr/efibootmgr_17-1_amd64.deb" -EFIBOOTMGR_CHECKSUM="0da33e43c97e5505d4d54e4145a72e76ce72278c3a9488792da86d9f30709d73" - -JQ_URL="http://archive.ubuntu.com/ubuntu/pool/universe/j/jq/jq_1.6-1ubuntu0.20.04.1_amd64.deb" -JQ_CHECKSUM="8e4c8223f6ec158dc6c2a0d065b76c337bb7664e35cbbecd2ad02142d3b83470" - -LIBJQ1_URL="http://archive.ubuntu.com/ubuntu/pool/universe/j/jq/libjq1_1.6-1ubuntu0.20.04.1_amd64.deb" -LIBJQ1_CHECKSUM="5aafb335442d3a694b28204e390c831de9efc3f3a18245328840d406edc8a163" - -LIBONIG5_URL="http://archive.ubuntu.com/ubuntu/pool/universe/libo/libonig/libonig5_6.9.4-1_amd64.deb" -LIBONIG5_CHECKSUM="041f5fb2dc781fcd1bcdc5a4115108a3ecca770c1cab69d5f25aafffe7842cf5" - -# Fixed timestamp for reproducible build -TOUCH_TIMESTAMP="200901031815.05" -XORRISO_TIMESTAMP="2009010318150500" -export SOURCE_DATE_EPOCH="1231006505" - -# Get keyword arguments -for argument in "${@}"; do - case ${argument} in - -d=* | --deployment=*) - DEPLOYMENT="${argument#*=}" - shift - ;; - -h | --help) - echo 'Usage: -SetupOS Builder - -Arguments: - -d=, --deployment= specify the deployment name (Default: mainnet) - --host-os= specify the HostOS disk-image file (Default: ./src/nocloud/host-os.img.tar.gz) - --guest-os= specify the GuestOS disk-image file (Default: ./src/nocloud/guest-os.img.tar.gz) - -h, --help show this help message and exit - -k=, --key= specify the NNS public key (Default: ./src/nocloud/nns_public_key.pem) - -l=, --logging= specify the logging hosts/destination (Default: telemetry01.mainnet.dfinity.network telemetry02.mainnet.dfinity.network telemetry03.mainnet.dfinity.network) - -n=, --nns-url= specify the NNS URL for the GuestOS (Default: ) - -o=, --output= ISO output directory (Default: ./build-out/) - -u=, --ubuntu= specify a folder containing the Ubuntu Live Server ISO image (Example: ./ubuntu/) - -s=, --name-servers= specify the DNS name servers (Default: 2606:4700:4700::1111 2606:4700:4700::1001 2001:4860:4860::8888 2001:4860:4860::8844) - --memory= specify the amount of memory in GiB (Gibibytes) for the GuestOS (Default: 490) -' - exit 1 - ;; - --host-os=*) - HOST_OS="${argument#*=}" - shift - ;; - --guest-os=*) - GUEST_OS="${argument#*=}" - shift - ;; - -k=* | --key=*) - KEY="${argument#*=}" - shift - ;; - -l=* | --logging=*) - LOGGING="${argument#*=}" - shift - ;; - -n=* | --nns-url=*) - NNS_URL="${argument#*=}" - shift - ;; - -o=* | --output=*) - OUTPUT="${argument#*=}" - shift - ;; - -s=* | --name-servers=*) - NAME_SERVERS="${argument#*=}" - shift - ;; - -u=* | --ubuntu=*) - UBUNTU="${argument#*=}" - shift - ;; - --memory=*) - MEMORY="${argument#*=}" - shift - ;; - *) - echo "Error: Argument is not supported: ${argument#*=}" - exit 1 - ;; - esac -done - -# Set arguments if undefined -DEPLOYMENT="${DEPLOYMENT:=mainnet}" -HOST_OS="${HOST_OS:=${BASE_DIR}/src/nocloud/host-os.img.tar.gz}" -GUEST_OS="${GUEST_OS:=${BASE_DIR}/src/nocloud/guest-os.img.tar.gz}" -KEY="${KEY:=${BASE_DIR}/src/nocloud/nns_public_key.pem}" -LOGGING="${LOGGING:=elasticsearch-node-0.mercury.dfinity.systems:443 elasticsearch-node-1.mercury.dfinity.systems:443 elasticsearch-node-2.mercury.dfinity.systems:443 elasticsearch-node-3.mercury.dfinity.systems:443}" -MEMORY="${MEMORY:=490}" -NNS_URL="${NNS_URL:=http://[2600:c02:b002:15:5000:ceff:fecc:d5cd]:8080,http://[2604:3fc0:3002:0:5000:6bff:feb9:6baf]:8080,http://[2a00:fb01:400:100:5000:5bff:fe6b:75c6]:8080,http://[2604:3fc0:2001:0:5000:b0ff:fe7b:ff55]:8080,http://[2600:3000:6100:200:5000:cbff:fe4b:b207]:8080,http://[2604:3fc0:3002:0:5000:4eff:fec2:4806]:8080,http://[2001:920:401a:1708:5000:5fff:fec1:9ddb]:8080,http://[2001:920:401a:1706:5000:87ff:fe11:a9a0]:8080,http://[2401:3f00:1000:24:5000:deff:fed6:1d7]:8080,http://[2a00:fb01:400:100:5000:61ff:fe2c:14ac]:8080,http://[2a04:9dc0:0:108:5000:ccff:feb7:c03b]:8080,http://[2600:c02:b002:15:5000:53ff:fef7:d3c0]:8080,http://[2401:3f00:1000:22:5000:c3ff:fe44:36f4]:8080,http://[2607:f1d0:10:1:5000:a7ff:fe91:44e]:8080,http://[2a04:9dc0:0:108:5000:96ff:fe4a:be10]:8080,http://[2604:7e00:50:0:5000:20ff:fea7:efee]:8080,http://[2600:3004:1200:1200:5000:59ff:fe54:4c4b]:8080,http://[2a0f:cd00:2:1:5000:3fff:fe36:cab8]:8080,http://[2401:3f00:1000:23:5000:80ff:fe84:91ad]:8080,http://[2607:f758:c300:0:5000:72ff:fe35:3797]:8080,http://[2607:f758:1220:0:5000:12ff:fe0c:8a57]:8080,http://[2a01:138:900a:0:5000:2aff:fef4:c47e]:8080,http://[2a0f:cd00:2:1:5000:87ff:fe58:ceba]:8080,http://[2401:3f00:1000:24:5000:86ff:fea6:9bb5]:8080,http://[2600:2c01:21:0:5000:27ff:fe23:4839]:8080,http://[2a04:9dc0:0:108:5000:7cff:fece:97d]:8080,http://[2001:920:401a:1708:5000:4fff:fe92:48f1]:8080,http://[2604:3fc0:3002:0:5000:acff:fe31:12e8]:8080,http://[2a04:9dc0:0:108:5000:6bff:fe08:5f57]:8080,http://[2607:f758:c300:0:5000:3eff:fe6d:af08]:8080,http://[2607:f758:1220:0:5000:bfff:feb9:6794]:8080,http://[2607:f758:c300:0:5000:8eff:fe8b:d68]:8080,http://[2607:f758:1220:0:5000:3aff:fe16:7aec]:8080,http://[2a00:fb01:400:100:5000:ceff:fea2:bb0]:8080,http://[2a00:fa0:3:0:5000:5aff:fe89:b5fc]:8080,http://[2a00:fa0:3:0:5000:68ff:fece:922e]:8080,http://[2600:3000:6100:200:5000:c4ff:fe43:3d8a]:8080,http://[2001:920:401a:1710:5000:d7ff:fe6f:fde7]:8080,http://[2a01:138:900a:0:5000:5aff:fece:cf05]:8080,http://[2600:3006:1400:1500:5000:20ff:fe3f:3c98]:8080}" -NAME_SERVERS="${NAME_SERVERS:=2606:4700:4700::1111 2606:4700:4700::1001 2001:4860:4860::8888 2001:4860:4860::8844}" -OUTPUT="${OUTPUT:=${BASE_DIR}/build-out}" - -function validate_input() { - local variable="${1}" - local message="${2}" - - if [ -z ${variable} ]; then - echo "Missing Argument:" - echo " ${message}" - exit 1 - fi -} - -function log_and_exit_on_error() { - local exit_code="${1}" - local log_message="${2}" - - if [ "${exit_code}" -ne 0 ]; then - echo "${log_message}" - exit "${exit_code}" - fi -} - -function log_start() { - TIME_START=$(date '+%s') - - echo "SetupOS Builder - Start" - log_and_exit_on_error "${?}" "Unable to start SetupOS builder." -} - -function validate_guest_os() { - echo "* Validating GuestOS disk-image..." - if [ ! -r "${GUEST_OS}" ]; then - log_and_exit_on_error "1" "Unable to find or read GuestOS disk-image." - fi -} - -function validate_host_os() { - echo "* Validating HostOS disk-image..." - if [ ! -r "${HOST_OS}" ]; then - log_and_exit_on_error "1" "Unable to find or read HostOS disk-image." - fi -} - -function validate_ubuntu_path() { - if [ -z "${UBUNTU}" ]; then - echo "* No Ubuntu Live Server ISO image specified." - UBUNTU_ISO_PATH="${TMP_DIR}" - else - echo "* Existing Ubuntu Live Server ISO image specified: ${UBUNTU}" - UBUNTU_ISO_PATH="${UBUNTU}" - fi -} - -function prepare_build_directories() { - if [ ! -d "${OUTPUT}" ]; then - echo "* Creating build directories..." - mkdir -p "${OUTPUT}" - fi -} - -function download_ubuntu_packages() { - echo "* Downloading Ubuntu packages..." - - curl --output "${TMP_DIR}/efibootmgr.deb" --location "${EFIBOOTMGR_URL}" - curl --output "${TMP_DIR}/libonig5.deb" --location "${LIBONIG5_URL}" - curl --output "${TMP_DIR}/libjq1.deb" --location "${LIBJQ1_URL}" - curl --output "${TMP_DIR}/jq.deb" --location "${JQ_URL}" -} - -function verify_ubuntu_packages() { - echo "* Verifying checksum of Ubuntu packages..." - - cd "${TMP_DIR}" - echo "${EFIBOOTMGR_CHECKSUM} *efibootmgr.deb" | shasum --algorithm 256 --check - echo "${LIBONIG5_CHECKSUM} *libonig5.deb" | shasum --algorithm 256 --check - echo "${LIBJQ1_CHECKSUM} *libjq1.deb" | shasum --algorithm 256 --check - echo "${JQ_CHECKSUM} *jq.deb" | shasum --algorithm 256 --check - cd - -} - -function download_ubuntu_iso() { - if [ -z "${UBUNTU}" ]; then - echo "* Downloading Ubuntu Live Server ISO image..." - curl -L --output "${TMP_DIR}/${UBUNTU_ISO}" ${UBUNTU_URL} - fi -} - -function verify_ubuntu_iso() { - echo "* Verifying Ubuntu Live Server ISO image checksum..." - - cd "${UBUNTU_ISO_PATH}" - echo "${UBUNTU_CHECKSUM} *${UBUNTU_ISO}" | shasum --algorithm 256 --check - cd - -} - -function extract_ubuntu_iso() { - echo "* Extracting Ubuntu Live Server ISO image..." - - # Extract Ubuntu ISO image except BOOT directory - 7z x ${UBUNTU_ISO_PATH}/${UBUNTU_ISO} -x'![BOOT]' -o${ISO_DIR} -} - -function prepare_build() { - echo "* Preparing SetupOS build..." - - # Create nocloud directory - mkdir -p "${ISO_DIR}/nocloud" - - # Copy Ubuntu packages to ISO - cp --preserve=timestamp "${TMP_DIR}/libonig5.deb" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${TMP_DIR}/libjq1.deb" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${TMP_DIR}/jq.deb" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${TMP_DIR}/efibootmgr.deb" "${ISO_DIR}/nocloud" - - # Copy setup files to ISO - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/meta-data" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/user-data" "${ISO_DIR}/nocloud" - - # Copy setup scripts to ISO - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/00_common.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/01_setupos.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/02_hardware.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/03_firmware.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/04_uefi.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/05_disk.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/06_hostos.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/07_guestos.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/08_devices.sh" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/09_setupos.sh" "${ISO_DIR}/nocloud" - - # Copy deployment.json, nns_public_key.pem to ISO - cp --preserve=timestamp "${BASE_DIR}/src/nocloud/deployment.json" "${ISO_DIR}/nocloud" - cp --preserve=timestamp "${KEY}" "${ISO_DIR}/nocloud/nns_public_key.pem" - - # Inject deployment configuration - sed -i "s@{{ nns_url }}@${NNS_URL}@g" "${ISO_DIR}/nocloud/deployment.json" - sed -i "s@{{ deployment_name }}@${DEPLOYMENT}@g" "${ISO_DIR}/nocloud/deployment.json" - sed -i "s@{{ logging_hosts }}@${LOGGING}@g" "${ISO_DIR}/nocloud/deployment.json" - sed -i "s@{{ dns_name_servers }}@${NAME_SERVERS}@g" "${ISO_DIR}/nocloud/deployment.json" - sed -i "s@{{ resources_memory }}@${MEMORY}@g" "${ISO_DIR}/nocloud/deployment.json" - - # Copy disk-image files - cp --preserve=timestamp "${GUEST_OS}" "${ISO_DIR}/nocloud/guest-os.img.tar.gz" - cp --preserve=timestamp "${HOST_OS}" "${ISO_DIR}/nocloud/host-os.img.tar.gz" - - # Update boot flags with cloud-init autoinstall - sed -i 's|---|autoinstall ds=nocloud\\\;s=/cdrom/nocloud/ ---|g' ${ISO_DIR}/boot/grub/grub.cfg - sed -i 's|---|autoinstall ds=nocloud;s=/cdrom/nocloud/ ---|g' ${ISO_DIR}/isolinux/txt.cfg - - # Disable autoinstall - rm -f ${ISO_DIR}/casper/installer.squashfs - - # Disable md5 checksum on boot - md5sum ${ISO_DIR}/dists/focal/Release >${ISO_DIR}/md5sum.txt - sed -i "s@${ISO_DIR}/@./@g" ${ISO_DIR}/md5sum.txt - - # Fix timestamps for reproducible build - touch -t ${TOUCH_TIMESTAMP} \ - ${ISO_DIR}/nocloud \ - ${ISO_DIR}/nocloud/deployment.json \ - ${ISO_DIR}/boot/grub \ - ${ISO_DIR}/boot/grub/grub.cfg \ - ${ISO_DIR}/isolinux/txt.cfg \ - ${ISO_DIR}/boot/grub \ - ${ISO_DIR}/boot/grub/grub.cfg \ - ${ISO_DIR}/isolinux/txt.cfg \ - ${ISO_DIR}/md5sum.txt \ - ${ISO_DIR}/casper \ - ${ISO_DIR}/isolinux \ - ${ISO_DIR} -} - -function create_config_partition() { - CONFIG_IMG="${TMP_DIR}/config.img" - truncate -s 8M ${CONFIG_IMG} - mkfs.vfat ${CONFIG_IMG} -} - -function create_config_templates() { - CONFIG_PART="${TMP_DIR}/config" - mkdir -p ${CONFIG_PART} - - ( - cat <"${CONFIG_PART}/config.ini" - - mkdir -p ${CONFIG_PART}/ssh_authorized_keys - - for file in admin backup readonly; do - ( - cat <"${CONFIG_PART}/ssh_authorized_keys/${file}" - done - - # Fix timestamps for reproducible build - touch -t ${TOUCH_TIMESTAMP} \ - ${CONFIG_PART}/config.ini \ - ${CONFIG_PART}/ssh_authorized_keys \ - ${CONFIG_PART}/ssh_authorized_keys/{admin,backup,readonly} - - mcopy -m -i "${TMP_DIR}/config.img" "${CONFIG_PART}/config.ini" :: - mcopy -sm -i "${TMP_DIR}/config.img" "${CONFIG_PART}/ssh_authorized_keys" :: -} - -function build_iso() { - echo "* Building SetupOS ISO image..." - - # Create ISO image from extracted Ubuntu ISO - xorriso -as mkisofs -r \ - -o ${OUTPUT}/setup-os.iso \ - -J -l -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \ - -boot-load-size 4 -boot-info-table \ - -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot \ - -isohybrid-gpt-basdat -isohybrid-apm-hfsplus \ - -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \ - -append_partition 3 0xef "${CONFIG_IMG}" \ - ${TMP_DIR}/iso/boot ${TMP_DIR}/iso \ - -- \ - -volume_date "c" "${XORRISO_TIMESTAMP}" \ - -volume_date "m" "${XORRISO_TIMESTAMP}" \ - -volume_date "x" "default" \ - -volume_date "f" "default" \ - -alter_date_r "b" "${XORRISO_TIMESTAMP}" / -- \ - -alter_date_r "c" "${XORRISO_TIMESTAMP}" / -- -} - -function remove_temporary_directory() { - echo "* Cleaning up build directories..." - - rm -rf ${TMP_DIR} - rm -rf ${BOOT_IMG} -} - -function log_end() { - local time_end=$(date '+%s') - local time_exec=$(expr "${time_end}" - "${TIME_START}") - local time_hr=$(date -d "1970-01-01 ${time_exec} sec" '+%H:%M:%S') - - echo "SetupOS Builder - End (${time_hr})" - log_and_exit_on_error "${?}" "Unable to end SetupOS builder." -} - -# Establish run order -function main() { - log_start - validate_guest_os - validate_host_os - validate_ubuntu_path - prepare_build_directories - download_ubuntu_packages - verify_ubuntu_packages - download_ubuntu_iso - verify_ubuntu_iso - extract_ubuntu_iso - prepare_build - create_config_partition - create_config_templates - build_iso - remove_temporary_directory - log_end -} - -main diff --git a/ic-os/setupos/scripts/get-artifact-version.sh b/ic-os/setupos/scripts/get-artifact-version.sh new file mode 100755 index 00000000000..f9fe05f85ca --- /dev/null +++ b/ic-os/setupos/scripts/get-artifact-version.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -eo pipefail + +source $(dirname "${BASH_SOURCE[0]}")/artifact-utils.sh + +INPUT_FILE="$1" + +version_from_disk_image "${INPUT_FILE}" diff --git a/ic-os/setupos/scripts/partitions.csv b/ic-os/setupos/scripts/partitions.csv new file mode 100644 index 00000000000..b06642a30a7 --- /dev/null +++ b/ic-os/setupos/scripts/partitions.csv @@ -0,0 +1,20 @@ +# Partitions specification +# +# Each row takes the form: +# name,start,size,type,uuid,comment +# where +# name: identifier used for the partition (see design doc) +# start: start of partition in sector units (512 bytes) +# size: size of partition in sector units (512 bytes) +# type: a typecode recognized by sfdisk +# uuid: uuid for partition (to ensure that the build is deterministic) +# comment: free-form field explaining the partition +# +# Note that "start" and "size" need to match up correctly, otherwise +# partition table building will fail. +esp , 2048, 204800,U,B78084E2-3363-1346-8C25-D426F26B8928,EFI system partition +grub , 206848, 204800,L,6788E4CF-F456-104E-9A34-A2C58CFB0EE6,Grub bootloader modules and config +config, 411648, 204800,M,A5BA3816-BEAA-D74D-993E-CFA5AA6BA1F6,External config +data, 616448, 4194304,L,E97BF7B1-9400-439F-93D0-13276111F989,External data +boot, 4810752, 1048576,L,DDF618FE-7244-B446-A175-3296E6B9D02E,Boot partition for system +root, 5859328,2097152,L,7C0A626E-E5EA-E543-B5C5-300EB8304DB7,Root partition for system diff --git a/ic-os/setupos/scripts/partitions.sh b/ic-os/setupos/scripts/partitions.sh new file mode 100755 index 00000000000..e11ccf0a834 --- /dev/null +++ b/ic-os/setupos/scripts/partitions.sh @@ -0,0 +1,97 @@ +# Tools for dealing with the partition table (defined in partitions.csv) +# and disk image building. + +# Prepare properly sized disk image with partitions set up as specified. +# +# Arguments: +# $1: The file where to generate the disk image in. +function prepare_disk_image() { + local DISK_IMAGE="$1" + truncate --size 0 "$DISK_IMAGE" + truncate --size 43310M "$DISK_IMAGE" + generate_sfdisk_script | sfdisk "$DISK_IMAGE" +} + +# Write a single partition image into a specific partition of the +# disk image. +# +# Arguments: +# $1: The disk image to write into. +# $2: The name of the partition that should be written. +# $3: The file with the contents of the partition to be written. +function write_single_partition() { + local DISK_IMAGE="$1" + local PART_NAME="$2" + local PART_IMAGE="$3" + + local FILE_SIZE=$(stat -c "%s" "$PART_IMAGE") + + local PART_START=${PARTITION_START_BY_NAME["$PART_NAME"]} + local PART_SIZE=${PARTITION_SIZE_BY_NAME["$PART_NAME"]} + local PART_SIZE_BYTES=$(("$PART_SIZE" * 512)) + + if [ "$FILE_SIZE" -gt "$PART_SIZE_BYTES" ]; then exit 1; fi + + dd if="$PART_IMAGE" of="$DISK_IMAGE" bs=512 seek="$PART_START" conv=sparse,notrunc +} + +# Extracts a single partition image from a disk image to a file. +# +# Arguments: +# $1: The disk image to be read from. +# $2: The name of the partition that should be read. +# $3: The file to which the partition contents should be. +function extract_single_partition() { + local DISK_IMAGE="$1" + local PART_NAME="$2" + local PART_IMAGE="$3" + + local FILE_SIZE=$(stat -c "%s" "$PART_IMAGE") + + local PART_START=$((${PARTITION_START_BY_NAME["$PART_NAME"]} / 8)) + local PART_SIZE=$((${PARTITION_SIZE_BY_NAME["$PART_NAME"]} / 8)) + + dd if="$DISK_IMAGE" of="$PART_IMAGE" bs=4096 skip="$PART_START" count="$PART_SIZE" conv=sparse +} + +# All of the below are helper functions -- should not be used directly. + +# Read partitions.csv in "canonical form" (i.e. remove all comments, +# strip whitespace from fields). +# Internal utility function. +function read_canonical_partitions_csv() { + local BASE_DIR=$(dirname "${BASH_SOURCE[0]}") + sed -e '/^#/d' -e 's/ *, */,/g' <"${BASE_DIR}"/partitions.csv +} + +# Produce an sfdisk script as output to set up the partition table +function generate_sfdisk_script() { + echo "label: gpt" + echo "label-id: 2B110BB7-CDEC-7D41-B97E-893EDCBE5428" + read_canonical_partitions_csv \ + | while IFS=, read -r name start size type uuid comment; do + if [ "$type" == "L" ]; then + type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 + elif [ "$type" == "U" ]; then + type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B + fi + echo start=$start,size=$size,type=$type,uuid=$uuid + done +} + +function read_partition_sizes_by_name() { + read_canonical_partitions_csv \ + | while IFS=, read -r name start size type uuid comment; do + echo "[$name]=$size" + done +} + +function read_partition_starts_by_name() { + read_canonical_partitions_csv \ + | while IFS=, read -r name start size type uuid comment; do + echo "[$name]=$start" + done +} + +eval "declare -A PARTITION_SIZE_BY_NAME=( $(read_partition_sizes_by_name) )" +eval "declare -A PARTITION_START_BY_NAME=( $(read_partition_starts_by_name) )" diff --git a/ic-os/setupos/shell.nix b/ic-os/setupos/shell.nix new file mode 100644 index 00000000000..bc77db9e5cb --- /dev/null +++ b/ic-os/setupos/shell.nix @@ -0,0 +1,17 @@ +{ pkgs ? import ../../nix { inherit system; } +, system ? builtins.currentSystem +}: +let + python3-packages = python-packages: []; + python3-with-packages = pkgs.python3.withPackages python3-packages; +in +pkgs.mkCiShell { + buildInputs = [ + pkgs.dosfstools + pkgs.fakeroot + pkgs.libtar + pkgs.mtools + pkgs.policycoreutils + python3-with-packages + ]; +} diff --git a/ic-os/setupos/src/nocloud/01_setupos.sh b/ic-os/setupos/src/nocloud/01_setupos.sh deleted file mode 100755 index e5bd5b225cf..00000000000 --- a/ic-os/setupos/src/nocloud/01_setupos.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -set -o nounset -set -o pipefail - -SHELL="/bin/bash" -PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -function install_ubuntu_packages() { - echo "* Installing Ubuntu packages..." - - apt install -y --no-install-recommends \ - /media/cdrom/nocloud/libonig5.deb \ - /media/cdrom/nocloud/libjq1.deb \ - /media/cdrom/nocloud/jq.deb >/dev/null 2>&1 - log_and_reboot_on_error "${?}" "Unable to install 'jq'." - - apt install -y --no-install-recommends \ - /media/cdrom/nocloud/efibootmgr.deb >/dev/null 2>&1 - log_and_reboot_on_error "${?}" "Unable to install 'efibootmgr'." -} - -# Establish run order -main() { - source /media/cdrom/nocloud/00_common.sh - log_start - install_ubuntu_packages - log_end -} - -main diff --git a/ic-os/setupos/src/nocloud/09_setupos.sh b/ic-os/setupos/src/nocloud/09_setupos.sh deleted file mode 100755 index 3296f1def57..00000000000 --- a/ic-os/setupos/src/nocloud/09_setupos.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -set -o nounset -set -o pipefail - -SHELL="/bin/bash" -PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -function reboot_setupos() { - echo "* Rebooting SetupOS..." - echo " " - echo " " - echo " " - echo " " - echo "-------------------------------------------------------------------------------" - echo " INTERNET COMPUTER SETUP WAS SUCCESSFUL" - echo "-------------------------------------------------------------------------------" - echo " " - echo " " - echo " " - echo " " - echo " Please do NOT unplug the Nitrokey HSM USB device just yet." - echo " " - echo " Wait for the message after the first boot..." - echo " " - echo " " - echo " " - echo " " - echo "-------------------------------------------------------------------------------" - echo " INTERNET COMPUTER SETUP WAS SUCCESSFUL" - echo "-------------------------------------------------------------------------------" - echo " " - echo " " - echo " " - echo " " - sleep 15 - shutdown -r now -} - -# Establish run order -main() { - source /media/cdrom/nocloud/00_common.sh - log_start - reboot_setupos - log_end -} - -main diff --git a/ic-os/setupos/src/nocloud/deployment.json b/ic-os/setupos/src/nocloud/deployment.json deleted file mode 100644 index ed2aaf7cf36..00000000000 --- a/ic-os/setupos/src/nocloud/deployment.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "deployment": { - "name": "{{ deployment_name }}" - }, - "logging": { - "hosts": "{{ logging_hosts }}" - }, - "nns": { - "url": "{{ nns_url }}" - }, - "dns": { - "name_servers": "{{ dns_name_servers }}" - }, - "resources": { - "memory": "{{ resources_memory }}" - } -} diff --git a/ic-os/setupos/src/nocloud/nns_public_key.pem b/ic-os/setupos/src/nocloud/nns_public_key.pem deleted file mode 100644 index 21ccf24074f..00000000000 --- a/ic-os/setupos/src/nocloud/nns_public_key.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGCMB0GDSsGAQQBgtx8BQMBAgEGDCsGAQQBgtx8BQMCAQNhAIFMDm7HH6tYOwi9 -gTc8JVw8NxsuhIY8mKTx4It0I10U+12cDNVG2WhfkToMCyzFNBWDv0tDkuRn25bW -W5u0y3FxEvhHLg1aTRRQX/10hLASkQkcX4e5iINGP5gJGguqrg== ------END PUBLIC KEY----- diff --git a/ic-os/setupos/src/nocloud/user-data b/ic-os/setupos/src/nocloud/user-data deleted file mode 100644 index e1b347b9976..00000000000 --- a/ic-os/setupos/src/nocloud/user-data +++ /dev/null @@ -1,26 +0,0 @@ -#cloud-config - -# Do not print SSH fingerprints to the console -no_ssh_fingerprints: false - -# Do not print SSH host keys to the console -ssh: - emit_keys_to_console: false - -# Do not configure network -network: - config: disabled -disable_network_activation: true - -runcmd: -- bash /media/cdrom/nocloud/01_setupos.sh -- bash /media/cdrom/nocloud/02_hardware.sh -# NOTE: Firmware up-/downgrades are currently applied manually -#- bash /media/cdrom/nocloud/03_firmware.sh -# NOTE: UEFI settings are currently applied manually -#- bash /media/cdrom/nocloud/04_uefi.sh -- bash /media/cdrom/nocloud/05_disk.sh -- bash /media/cdrom/nocloud/06_hostos.sh -- bash /media/cdrom/nocloud/07_guestos.sh -- bash /media/cdrom/nocloud/08_devices.sh -- bash /media/cdrom/nocloud/09_setupos.sh diff --git a/toolchains/sysimage/build_disk_image.py b/toolchains/sysimage/build_disk_image.py index 23e170ec341..11439bab4cb 100755 --- a/toolchains/sysimage/build_disk_image.py +++ b/toolchains/sysimage/build_disk_image.py @@ -54,7 +54,7 @@ def validate_partition_table(gpt_entries): raise RuntimeError("Partition %s start is not aligned to 1MB boundary" % entry["name"]) if (entry["size"] % 2048) != 0: raise RuntimeError("Partition %s size is not aligned to 1MB boundary" % entry["name"]) - if not entry["type"] in ("U", "L"): + if not entry["type"] in ("U", "L", "M"): raise RuntimeError("Partition %s has unsupported type" % entry["name"]) end = entry["start"] + entry["size"] @@ -65,6 +65,8 @@ def generate_sfdisk_script(gpt_entries): "U": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", # Linux partition type "L": "0FC63DAF-8483-4772-8E79-3D69D8477DE4", + # Microsoft basic data + "M": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", } lines = ["label: gpt", "label-id: 2B110BB7-CDEC-7D41-B97E-893EDCBE5428"] for entry in gpt_entries: diff --git a/toolchains/sysimage/build_fat32_image.py b/toolchains/sysimage/build_fat32_image.py new file mode 100755 index 00000000000..040153e4280 --- /dev/null +++ b/toolchains/sysimage/build_fat32_image.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Packs contents of a tar file into a fat32 image (possibly taking only a +# subdirectory of the full tar file). The (sparse) fat32 image itself is then +# wrapped into a tar file itself. +# +# Call example: +# build_fat32_image -s 10M -o partition.img.tar -p boot/efi -i dockerimg.tar +# +import argparse +import atexit +import os +import shutil +import subprocess +import sys +import tarfile +import tempfile + + +def untar_to_fat32(tf, fs_basedir, out_file, path_transform): + """ + Put contents of tarfile into fat32 image. + + Take all files in the given input tarfile "tf", and put them + into the fat32 image pointed to by "out_file". "fs_basedir" is + used as temporary directory to unpack files to. + + path_transform converts the paths in the tarfile into target + paths on the system (if it returns "None" for any input path, + then the corresponding file/dir is dropped). + """ + for member in tf: + path = path_transform(member.path) + if path is None or path == "": + continue + if path[0] == "/": + path = path[1:] + if member.type == tarfile.DIRTYPE: + if path == "": + continue + os.mkdir(os.path.join(fs_basedir, path)) + subprocess.run(["faketime", "1970-1-1 0", "mmd", "-i", out_file, "::/" + path], check=True) + elif member.type == tarfile.REGTYPE or member.type == tarfile.AREGTYPE: + with open(os.path.join(fs_basedir, path), "wb") as f: + f.write(tf.extractfile(member).read()) + subprocess.run( + ["faketime", "1970-1-1 0", "mcopy", "-o", "-i", out_file, os.path.join(fs_basedir, path), "::/" + path], + check=True, + ) + else: + raise RuntimeError("Unhandled tar member kind: %s" % member.type) + + +def install_extra_files(out_file, extra_files, path_transform): + for extra_file in extra_files: + source_file, install_target, mode = extra_file.split(":") + if install_target[0] == "/": + install_target = install_target[1:] + subprocess.run( + [ + "faketime", + "1970-1-1 0", + "mcopy", + "-o", + "-i", + out_file, + source_file, + "::/" + path_transform(install_target), + ], + check=True, + ) + + +def parse_size(s): + if s[-1] == "k" or s[-1] == "K": + return 1024 * int(s[:-1]) + elif s[-1] == "m" or s[-1] == "M": + return 1024 * 1024 * int(s[:-1]) + elif s[-1] == "g" or s[-1] == "G": + return 1024 * 1024 * 1024 * int(s[:-1]) + else: + return int(s) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-l", "--label", help="Label to add to partition", type=str) + parser.add_argument("-s", "--size", help="Size of image to build", type=str) + parser.add_argument("-o", "--output", help="Target (tar) file to write partition image to", type=str) + parser.add_argument( + "-i", "--input", help="Source (tar) file to take files from", type=str, default="", required=False + ) + parser.add_argument( + "-p", + "--path", + help="Path to extract from tar file (only files below will be put into image", + required=False, + default="", + type=str, + ) + parser.add_argument( + "extra_files", + metavar="extra_files", + type=str, + nargs="*", + help="Extra files to install; expects list of sourcefile:targetfile:mode", + ) + + args = parser.parse_args(sys.argv[1:]) + + in_file = args.input + out_file = args.output + image_size = parse_size(args.size) + image_label = args.label + limit_prefix = args.path + extra_files = args.extra_files + + tmpdir = tempfile.mkdtemp() + atexit.register(lambda: shutil.rmtree(tmpdir)) + + fs_basedir = os.path.join(tmpdir, "fs") + os.mkdir(fs_basedir) + + image_file = os.path.join(tmpdir, "partition.img") + + def path_transform(path, limit_prefix=limit_prefix): + if path.startswith(limit_prefix): + return path[len(limit_prefix) :] + else: + return None + + os.close(os.open(image_file, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC | os.O_EXCL, 0o600)) + os.truncate(image_file, image_size) + subprocess.run(["/usr/sbin/mkfs.fat", "-F", "32", "-i", "0", image_file], check=True) + if image_label: + subprocess.run(["/usr/sbin/fatlabel", image_file, image_label], check=True) + + if in_file: + with tarfile.open(in_file, mode="r|*") as tf: + untar_to_fat32(tf, fs_basedir, image_file, path_transform) + + install_extra_files(image_file, extra_files, path_transform) + + subprocess.run( + [ + "tar", + "cf", + out_file, + "--sort=name", + "--owner=root:0", + "--group=root:0", + "--mtime=UTC 1970-01-01 00:00:00", + "--sparse", + "-C", + tmpdir, + "partition.img", + ], + check=True, + ) + + +if __name__ == "__main__": + main() diff --git a/toolchains/sysimage/docker_tar.py b/toolchains/sysimage/docker_tar.py index 88756cab9dd..245df769d33 100755 --- a/toolchains/sysimage/docker_tar.py +++ b/toolchains/sysimage/docker_tar.py @@ -286,6 +286,9 @@ def tar_fs(fs, outfile): def make_argparser(): parser = argparse.ArgumentParser() + parser.add_argument( + "-s", "--skip-pull", help="Don't attempt to pull image from dockerhub.", default=False, action="store_true" + ) parser.add_argument("-o", "--output", help="Target (tar) file to write to", type=str) parser.add_argument( "build_args", @@ -304,7 +307,8 @@ def main(): build_args = list(args.build_args) # Build the docker image. - build_args.append("--pull") + if not args.skip_pull: + build_args.append("--pull") if any( [ os.environ.get("CI_JOB_NAME", "").startswith("docker-build-ic"),