remove aws image

This commit is contained in:
Leonard Cohnen 2022-04-19 12:12:34 +02:00 committed by 3u13r
parent 990ca20469
commit 4f6af8d304
21 changed files with 9 additions and 228 deletions

144
image/Makefile Normal file
View file

@ -0,0 +1,144 @@
SRC_PATH = $(CURDIR)
BASE_PATH ?= $(SRC_PATH)
DEPENDENCIES ?= $(BASE_PATH)/dependencies
COREOS_BUILD_PATH ?= $(BASE_PATH)/build
OVERRIDES ?= $(COREOS_BUILD_PATH)/overrides
OVERRIDES_RPM ?= $(OVERRIDES)/rpm
OVERRIDES_ROOTFS ?= $(OVERRIDES)/rootfs
COORDINATOR_OVERRIDE_PATH ?= $(OVERRIDES_ROOTFS)/usr/bin/coordinator
KERNEL_RPM ?= $(OVERRIDES_RPM)/kernel.rpm
KERNEL_CORE_RPM ?= $(OVERRIDES_RPM)/kernel-core.rpm
KERNEL_MODULES_RPM ?= $(OVERRIDES_RPM)/kernel-modules.rpm
OVERRIDE_KERNEL ?= n
KERNEL_RPM_URL ?= "https://kojipkgs.fedoraproject.org/packages/kernel/5.17.0/0.rc3.89.fc36/x86_64/kernel-5.17.0-0.rc3.89.fc36.x86_64.rpm"
KERNEL_CORE_RPM_URL ?= "https://kojipkgs.fedoraproject.org/packages/kernel/5.17.0/0.rc3.89.fc36/x86_64/kernel-core-5.17.0-0.rc3.89.fc36.x86_64.rpm"
KERNEL_MODULES_RPM_URL ?= "https://kojipkgs.fedoraproject.org/packages/kernel/5.17.0/0.rc3.89.fc36/x86_64/kernel-modules-5.17.0-0.rc3.89.fc36.x86_64.rpm"
DOWNLOAD_COORDINATOR ?= y
COORDINATOR_BINARY ?= $(DEPENDENCIES)/coordinator
COORDINATOR_URL ?= https://public-edgeless-constellation.s3.us-east-2.amazonaws.com/coordinator/coordinator-090232f06302957f47ab86207bd96f413eda07d9534afc12524a97363b7d203d863d52cbb1780fb76c6874df6df0387f89021b3140d5769d6a8aec1739515a66
DISK_MAPPER_BINARY ?= $(BASE_PATH)/../../build/disk-mapper
DISK_MAPPER_OVERRIDE_PATH ?= $(OVERRIDES_ROOTFS)/usr/sbin/disk-mapper
IMAGES_PATH ?= $(BASE_PATH)/images
CONTAINER_ENGINE ?= podman
COSA_ENV ?= $(BASE_PATH)/$(CONTAINER_ENGINE).env
COSA_INIT_REPO ?= "https://github.com/edgelesssys/constellation-fedora-coreos-config"
COSA_INIT_BRANCH ?= "constellation"
COSA_LOCKFILE ?= $(BASE_PATH)/cosa.lock
NETRC ?= ~/.netrc
GCP_BUCKET ?= constellation-images
GCP_IMAGE_NAME ?= constellation-coreos
GCP_IMAGE_FILENAME ?= $(GCP_IMAGE_NAME)_gcp.tar.gz
GCP_IMAGE_PATH ?= $(IMAGES_PATH)/$(GCP_IMAGE_FILENAME)
GCP_REGION ?= europe-west3
AZURE_IMAGE_NAME ?= constellation-coreos
AZURE_IMAGE_FILENAME ?= $(AZURE_IMAGE_NAME)_azure.vhd
AZURE_IMAGE_PATH ?= $(IMAGES_PATH)/$(AZURE_IMAGE_FILENAME)
AZURE_RESOURCE_GROUP_NAME ?= CONSTELLATION-IMAGES
AZURE_REGION ?= northeurope
AZURE_GALLERY_NAME ?= Constellation
AZURE_IMAGE_DEFINITION ?= $(AZURE_IMAGE_NAME)
AZURE_IMAGE_OFFER ?= $(AZURE_IMAGE_DEFINITION)
AZURE_IMAGE_VERSION ?= 0.0.1
AZURE_PUBLISHER ?= edgelesssys
AZURE_SKU ?= constellation-coreos
.PHONY: clean all kernel coreos run shell cosa-init cosa-fetch images image-gcp upload-gcp image-azure upload-azure-non-cvm $(COORDINATOR_OVERRIDE_PATH) $(DISK_MAPPER_OVERRIDE_PATH)
SHELL := /bin/bash
all: coreos images
ifeq ($(DOWNLOAD_COORDINATOR), y)
$(COORDINATOR_BINARY):
wget -q $(COORDINATOR_URL) -O $@
endif
$(COORDINATOR_OVERRIDE_PATH): $(COORDINATOR_BINARY) | cosa-init
mkdir -p $(COREOS_BUILD_PATH)/overrides/rootfs/usr/bin
cp $(COORDINATOR_BINARY) $@
chmod +x $@
$(DISK_MAPPER_OVERRIDE_PATH): $(DISK_MAPPER_BINARY) | cosa-init
mkdir -p $(COREOS_BUILD_PATH)/overrides/rootfs/usr/sbin
cp $(DISK_MAPPER_BINARY) $@
chmod +x $@
cosa-init:
-flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa init --branch $(COSA_INIT_BRANCH) $(COSA_INIT_REPO)'
cosa-fetch: cosa-init kernel $(COORDINATOR_OVERRIDE_PATH) $(DISK_MAPPER_OVERRIDE_PATH)
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa fetch'
coreos: cosa-fetch $(COORDINATOR_OVERRIDE_PATH) $(DISK_MAPPER_OVERRIDE_PATH)
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa build'
run: $(COORDINATOR_OVERRIDE_PATH) $(DISK_MAPPER_OVERRIDE_PATH)
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa run --devshell-console'
shell: $(COORDINATOR_OVERRIDE_PATH) $(DISK_MAPPER_OVERRIDE_PATH)
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa shell'
$(GCP_IMAGE_PATH): coreos
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa buildextend-gcp'
cp $(COREOS_BUILD_PATH)/builds/latest/x86_64/fedora-coreos-*.dev.*-gcp.x86_64.tar.gz $@
$(BASE_PATH)/dm-verity/recalculate-dm-verity.sh gcp $@
$(AZURE_IMAGE_PATH): coreos
flock $(COSA_LOCKFILE) -c '. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && NETRC=$(NETRC) cosa buildextend-azure'
cp $(COREOS_BUILD_PATH)/builds/latest/x86_64/fedora-coreos-*.dev.*-azure.x86_64.vhd $@
$(BASE_PATH)/dm-verity/recalculate-dm-verity.sh azure $@
images: image-gcp image-azure
image-gcp: $(GCP_IMAGE_PATH)
upload-gcp: $(GCP_IMAGE_PATH)
-gsutil mb -l $(GCP_REGION) gs://$(GCP_BUCKET)
gsutil pap set enforced gs://$(GCP_BUCKET)
gsutil cp $(GCP_IMAGE_PATH) gs://$(GCP_BUCKET)
gcloud compute images create $(GCP_IMAGE_NAME) --source-uri=gs://$(GCP_BUCKET)/$(GCP_IMAGE_FILENAME) --guest-os-features=GVNIC,SEV_CAPABLE,VIRTIO_SCSI_MULTIQUEUE,UEFI_COMPATIBLE --labels=coordinator-sha1=$$(shasum $(COORDINATOR_OVERRIDE_PATH) | cut -d " " -f 1),coordinator-sha512=$$(sha512sum $(COORDINATOR_OVERRIDE_PATH) | cut -d " " -f 1 | cut -c-63)
gsutil rm gs://$(GCP_BUCKET)/$(GCP_IMAGE_FILENAME)
image-azure: $(AZURE_IMAGE_PATH)
# reference: https://docs.microsoft.com/en-us/azure/virtual-machines/linux/disks-upload-vhd-to-managed-disk-cli
# reference: https://docs.microsoft.com/en-us/azure/virtual-machines/linux/upload-vhd
# reference: https://docs.microsoft.com/en-us/azure/virtual-machines/image-version
upload-azure: $(AZURE_IMAGE_PATH)
$(eval SIZE := $(shell cat $(AZURE_IMAGE_PATH) | wc -c))
@echo "creating disk of size $(SIZE)"
az disk create -n $(AZURE_IMAGE_NAME) -g $(AZURE_RESOURCE_GROUP_NAME) -l $(AZURE_REGION) --hyper-v-generation V2 --os-type Linux --for-upload --upload-size-bytes $(SIZE) --sku standard_lrs --tags coordinator-sha512=$$(sha512sum $(COORDINATOR_OVERRIDE_PATH) | cut -d " " -f 1)
@echo "waiting for disk to be created"
az disk wait --created -n $(AZURE_IMAGE_NAME) -g $(AZURE_RESOURCE_GROUP_NAME)
@echo "created disk $(AZURE_IMAGE_NAME)"
az disk list --output table --query "[?name == '$(AZURE_IMAGE_NAME)' && resourceGroup == '$(AZURE_RESOURCE_GROUP_NAME)']"
@echo "Granting temporary write access on disk $(AZURE_IMAGE_NAME)"
@echo "Copying $(AZURE_IMAGE_PATH) to disk $(AZURE_IMAGE_NAME)"
azcopy copy "$(AZURE_IMAGE_PATH)" "$$(az disk grant-access -n $(AZURE_IMAGE_NAME) -g $(AZURE_RESOURCE_GROUP_NAME) --access-level Write --duration-in-seconds 86400 | jq -r .accessSas)" --blob-type PageBlob
@echo "Revoking write access"
az disk revoke-access -n $(AZURE_IMAGE_NAME) -g $(AZURE_RESOURCE_GROUP_NAME)
az image create -g $(AZURE_RESOURCE_GROUP_NAME) -l $(AZURE_REGION) -n $(AZURE_IMAGE_NAME) --hyper-v-generation V2 --os-type Linux --tags coordinator-sha512=$$(sha512sum $(COORDINATOR_OVERRIDE_PATH) | cut -d " " -f 1) --source "$$(az disk list --query "[?name == '$(AZURE_IMAGE_NAME)' && resourceGroup == '$(AZURE_RESOURCE_GROUP_NAME)'] | [0].id" --output json | jq -r)"
@echo "Create shared image gallery (if it does not exist yet)"
-az sig create -l $(AZURE_REGION) --gallery-name $(AZURE_GALLERY_NAME) --resource-group $(AZURE_RESOURCE_GROUP_NAME)
@echo "Create image definition (if it does not exist yet)"
-az sig image-definition create --resource-group $(AZURE_RESOURCE_GROUP_NAME) -l $(AZURE_REGION) --gallery-name $(AZURE_GALLERY_NAME) --gallery-image-definition $(AZURE_IMAGE_DEFINITION) --publisher $(AZURE_PUBLISHER) --offer $(AZURE_IMAGE_OFFER) --sku $(AZURE_SKU) --os-type Linux --os-state generalized --hyper-v-generation V2 --features SecurityType=TrustedLaunch
az sig image-version create --resource-group $(AZURE_RESOURCE_GROUP_NAME) -l $(AZURE_REGION) --gallery-name $(AZURE_GALLERY_NAME) --gallery-image-definition $(AZURE_IMAGE_DEFINITION) --gallery-image-version $(AZURE_IMAGE_VERSION) --target-regions $(AZURE_REGION) --replica-count 1 --tags coordinator-sha512=$$(sha512sum $(COORDINATOR_OVERRIDE_PATH) | cut -d " " -f 1) --managed-image "$$(az image list --query "[?name == '$(AZURE_IMAGE_NAME)' && resourceGroup == '$(AZURE_RESOURCE_GROUP_NAME)'] | [0].id" --output json | jq -r)"
ifeq ($(OVERRIDE_KERNEL), y)
kernel: $(KERNEL_RPM) $(KERNEL_CORE_RPM) $(KERNEL_MODULES_RPM)
$(KERNEL_RPM): | cosa-init
-mkdir -p $$(dirname $@)
wget -q $(KERNEL_RPM_URL) -O $@
$(KERNEL_CORE_RPM): | cosa-init
-mkdir -p $$(dirname $@)
wget -q $(KERNEL_CORE_RPM_URL) -O $@
$(KERNEL_MODULES_RPM): | cosa-init
-mkdir -p $$(dirname $@)
wget -q $(KERNEL_MODULES_RPM_URL) -O $@
else
kernel:
endif
clean:
-rm $(COSA_LOCKFILE)
-rm $(IMAGES_PATH)/*
-. $(COSA_ENV) && cd $(COREOS_BUILD_PATH) && cosa clean

51
image/assembler/README.md Normal file
View file

@ -0,0 +1,51 @@
# Constellation-OS Assembler
This container image uses [coreos assembler](https://coreos.github.io/coreos-assembler/) as a base (`quay.io/coreos-assembler/coreos-assembler`) to facilitate a build environment for the Constellation-OS.
The root filesystem takes its contents (OSTree) from [constellation-fedora-coreos-config](https://github.com/edgelesssys/constellation-fedora-coreos-config).
The constellation specific changes are tracked in this repository: https://github.com/edgelesssys/constellation-coreos-assembler
And the Constellation-OS Assembler can be pulled from ghcr.io/edgelesssys/constellation-coreos-assembler
## Setup
Prerequisites: `podman` and `qemu-kvm` are installed, nested virtualization is enabled.
Make sure your user is allowed read and write access on `/dev/kvm`.
If the device is not mounted in the container try the following command, and restart the container:
``` shell
sudo chmod 666 /dev/kvm
```
## Using the Assembler to create a bootable operating system
1. Create the assembler image as described [here](#creating-the-assembler-image)
2. Source the `fcos/.env` file to enable the `cosa` bash alias:
```
source fcos/.env
```
3. Set the `COORDINATOR_BINARY` environment variable to a path of the compiled coordinator binary. It will be mounted in the cosa container and copied into the resulting coreos image.
```
COORDINATOR_BINARY="/path/to/coordinator"
```
4. Go into the build folder and initialize cosa:
```
cd fcos/build
cosa init https://github.com/edgelesssys/constellation-fedora-coreos-config
cosa fetch
```
5. Build the OS image:
```
cosa build
```
6. Create an image for a cloud provider
```
cosa buildextend-gcp
cosa buildextend-aws
cosa buildextend-azure
[...]
```
## Using a locally checked out git repo of the coreos-config during development
Simply set the environment variable `COREOS_ASSEMBLER_CONFIG_GIT` to the local folder and perform the rest of the steps as usual:
```
COREOS_ASSEMBLER_CONFIG_GIT=/path/to/constellation-fedora-coreos-config
```

0
image/build/.gitkeep Normal file
View file

View file

View file

@ -0,0 +1,191 @@
#!/usr/bin/env bash
set -euo pipefail
# Show progress on pipes if `pv` is installed
# Otherwise use plain cat
if ! command -v pv &> /dev/null
then
PV="cat"
else
PV="pv"
fi
mount_partition () {
local partition_file=$1
local mountpoint=$2
# use guestmount if possible
if command -v guestmount &> /dev/null && guestmount -a "${partition_file}" -m /dev/sda "${mountpoint}"; then
return
fi
# second, try to mount as current user
if mount -o loop "${partition_file}" "${mountpoint}"; then
return
fi
# third, try to mount with sudo
sudo mount -o loop "${partition_file}" "${mountpoint}"
# temporarily change ownership of partition files
sudo chown -R "${USER}:${USER}" "${mountpoint}"
}
umount_partition () {
local mountpoint=$1
# use guestunmount if possible
if command -v guestunmount &> /dev/null && guestunmount "${mountpoint}"; then
return
fi
# second, try to umount as current user
if umount "${mountpoint}"; then
return
fi
# third, try to umount with sudo
# repair ownership of partition files
sudo chown -R root:root "${mountpoint}"
sudo umount "${mountpoint}"
}
# Unpacks finished cloud provider image to recalculate dm-verity hash
unpack () {
local cloudprovider=$1
local packed_image=$2
local unpacked_image=$3
case $cloudprovider in
gcp)
echo "📤 Unpacking GCP image..."
"${PV}" "$packed_image" | tar -xzf - -O > "$unpacked_image"
echo " Unpacked image stored in ${unpacked_image}"
;;
azure)
echo "📤 Unpacking Azure image..."
qemu-img convert -p -f vpc -O raw "$packed_image" "$unpacked_image"
echo " Unpacked image stored in ${unpacked_image}"
;;
*)
echo "unknown cloud provider"
exit 1
;;
esac
}
get_part_offset () {
local unpacked_image=$1
local part_number=$2
local offset
offset=$(parted -s "${unpacked_image}" unit s print | sed 's/^ //g' | grep "^${part_number}" | tr -s ' ' | cut -d ' ' -f2)
local offset=${offset::-1}
echo "${offset}"
}
get_part_size () {
local unpacked_image=$1
local part_number=$2
local size
size=$(parted -s "${unpacked_image}" unit s print | sed 's/^ //g' | grep "^${part_number}" | tr -s ' ' | cut -d ' ' -f4)
local size=${size::-1}
echo "${size}"
}
extract_partition () {
local unpacked_image=$1
local part_number=$2
local extracted_partition_path=$3
local part_offset
part_offset=$(get_part_offset "${unpacked_image}" "${part_number}")
local part_size
part_size=$(get_part_size "${unpacked_image}" "${part_number}")
dd status=progress "if=${unpacked_image}" "of=${extracted_partition_path}" bs=512 "skip=${part_offset}" "count=${part_size}" 2>/dev/null
}
overwrite_partition () {
local unpacked_image=$1
local part_number=$2
local extracted_partition_path=$3
local part_offset
part_offset=$(get_part_offset "${unpacked_image}" "${part_number}")
local part_size
part_size=$(get_part_size "${unpacked_image}" "${part_number}")
dd status=progress conv=notrunc "if=${extracted_partition_path}" "of=${unpacked_image}" bs=512 "seek=${part_offset}" "count=${part_size}" 2>/dev/null
}
update_verity () {
local tmp_dir=$1
local raw_image=$2
local boot_mountpoint=${tmp_dir}/boot.mount
local boot_partition=${tmp_dir}/part_boot.raw
local root_partition=${tmp_dir}/part_root.raw
local hashtree_partition=${tmp_dir}/part_hashtree.raw
echo "⬅️ Extracting partitions..."
extract_partition "${raw_image}" 3 "${boot_partition}"
extract_partition "${raw_image}" 4 "${root_partition}"
extract_partition "${raw_image}" 5 "${hashtree_partition}"
# recalculate verity hashtree
veritysetup_out=$(veritysetup format "${root_partition}" "${hashtree_partition}")
roothash=$(echo "${veritysetup_out}" | grep 'Root hash:' | sed --expression='s/Root hash:\s*//g')
echo "🧮 Recalculated dm-verity hashtree with roothash ${roothash}"
# update bootloader kernel cmdline
mkdir -p "${boot_mountpoint}"
mount_partition "${boot_partition}" "${boot_mountpoint}"
sed -i -r "s/verity.sysroot=[[:xdigit:]]+/verity.sysroot=${roothash}/g" "${boot_mountpoint}/loader.1/entries/ostree-1-fedora-coreos.conf"
echo "✍️ Updated bootloader kernel cmdline to include new dm-verity roothash: $(grep '^options ' "${boot_mountpoint}"/loader.1/entries/ostree-1-fedora-coreos.conf)"
umount_partition "${boot_mountpoint}"
rmdir "${boot_mountpoint}"
echo "➡️ Overwriting partitions..."
overwrite_partition "${raw_image}" 3 "${boot_partition}"
overwrite_partition "${raw_image}" 5 "${hashtree_partition}"
}
repack () {
local cloudprovider=$1
local unpacked_image=$2
local packed_image=$3
local unpacked_image_dir
unpacked_image_dir=$(dirname "${unpacked_image}")
local unpacked_image_filename
unpacked_image_filename=$(basename "${unpacked_image}")
local tmp_tar_file
tmp_tar_file=$(mktemp -t verity.XXXXXX.tar)
case $cloudprovider in
gcp)
echo "📥 Repacking GCP image..."
tar --owner=0 --group=0 -C "${unpacked_image_dir}" -Sch --format=oldgnu -f "${tmp_tar_file}" "${unpacked_image_filename}"
"${PV}" "${tmp_tar_file}" | pigz -9c > "${packed_image}"
rm "${tmp_tar_file}"
echo " Repacked image stored in ${packed_image}"
;;
azure)
echo "📥 Repacking Azure image..."
qemu-img convert -p -f raw -O vpc -o force_size,subformat=fixed "${unpacked_image}" "$packed_image"
echo " Repacked image stored in ${packed_image}"
;;
*)
echo "unknown cloud provider"
exit 1
;;
esac
}
echo "🔁 Recalculating dm-verity hashtree 🌳"
tmp_dir=$(mktemp -d -t verity-XXXXXXXXXX)
raw_image="${tmp_dir}/disk.raw"
unpack "$1" "$2" "${raw_image}"
update_verity "${tmp_dir}" "${raw_image}"
repack "$1" "${raw_image}" "${2}"
rm -r "${tmp_dir}"

15
image/docker.env Normal file
View file

@ -0,0 +1,15 @@
cosa() {
env | grep COREOS_ASSEMBLER
local -r COREOS_ASSEMBLER_CONTAINER_LATEST="ghcr.io/edgelesssys/constellation-coreos-assembler:latest"
test -t 1 && USE_TTY="-ti"
set -x
docker run --rm ${USE_TTY} --security-opt label=disable --privileged \
-v ${PWD}:/srv/ --device /dev/kvm --device /dev/fuse \
--tmpfs /tmp -v /var/tmp:/var/tmp --name constellation-cosa \
${NETRC+-v ${NETRC}:/home/builder/.netrc:ro} \
${COREOS_ASSEMBLER_CONFIG_GIT:+-v $COREOS_ASSEMBLER_CONFIG_GIT:/srv/src/config/:ro} \
${COREOS_ASSEMBLER_GIT:+-v $COREOS_ASSEMBLER_GIT/src/:/usr/lib/coreos-assembler/:ro} \
${COREOS_ASSEMBLER_CONTAINER_RUNTIME_ARGS} \
${COREOS_ASSEMBLER_CONTAINER:-$COREOS_ASSEMBLER_CONTAINER_LATEST} "$@"
rc=$?; set +x; return $rc
}

208
image/docs/README.md Normal file
View file

@ -0,0 +1,208 @@
# Fedora CoreOS Assembler
We use the [Fedora CoreOS Assembler](https://coreos.github.io/coreos-assembler/) to build the base image for Constellation nodes.
## Setup
Prerequisites: `podman` and `qemu-kvm` are installed, nested virtualization is enabled.
Make sure your user is allowed read and write access on `/dev/kvm`.
If the device is not mounted in the container try the following command, and restart the container:
``` shell
sudo chmod 666 /dev/kvm
```
* Pull the assembler container image
``` shell
podman pull quay.io/coreos-assembler/coreos-assembler
```
* Create a working directory on your host system
``` shell
mkdir fcos && cd fcos
```
* Set up a bash alias
Add the following to your `.bashrc` to easily start the image assembler using `cosa`:
``` bash
cosa() {
env | grep COREOS_ASSEMBLER
local -r COREOS_ASSEMBLER_CONTAINER_LATEST="quay.io/coreos-assembler/coreos-assembler:latest"
if [[ -z ${COREOS_ASSEMBLER_CONTAINER} ]] && $(podman image exists ${COREOS_ASSEMBLER_CONTAINER_LATEST}); then
local -r cosa_build_date_str="$(podman inspect -f "{{.Created}}" ${COREOS_ASSEMBLER_CONTAINER_LATEST} | awk '{print $1}')"
local -r cosa_build_date="$(date -d ${cosa_build_date_str} +%s)"
if [[ $(date +%s) -ge $((cosa_build_date + 60*60*24*7)) ]] ; then
echo -e "\e[0;33m----" >&2
echo "The COSA container image is more that a week old and likely outdated." >&2
echo "You should pull the latest version with:" >&2
echo "podman pull ${COREOS_ASSEMBLER_CONTAINER_LATEST}" >&2
echo -e "----\e[0m" >&2
sleep 10
fi
fi
set -x
podman run --rm -ti --security-opt label=disable --privileged \
--uidmap=1000:0:1 --uidmap=0:1:1000 --uidmap 1001:1001:64536 \
-v ${PWD}:/srv/ --device /dev/kvm --device /dev/fuse \
--tmpfs /tmp -v /var/tmp:/var/tmp --name cosa \
${COREOS_ASSEMBLER_CONFIG_GIT:+-v $COREOS_ASSEMBLER_CONFIG_GIT:/srv/src/config/:ro} \
${COREOS_ASSEMBLER_GIT:+-v $COREOS_ASSEMBLER_GIT/src/:/usr/lib/coreos-assembler/:ro} \
${COREOS_ASSEMBLER_CONTAINER_RUNTIME_ARGS} \
${COREOS_ASSEMBLER_CONTAINER:-$COREOS_ASSEMBLER_CONTAINER_LATEST} "$@"
rc=$?; set +x; return $rc
}
```
* Run the builder
``` shell
cosa shell
```
* Initialize the build
``` shell
cosa init https://github.com/coreos/fedora-coreos-config
```
* Fetch metadata and packages
``` shell
cosa fetch
```
* Build a qemu VM image
``` shell
cosa build
```
Each build will create a new directory in `$PWD/builds/`, containing the generated OSTree commit and the qemu VM image.
* Run the image
``` shell
cosa run
```
## Customization
The CoreOS Assembler offers three main customization options:
* [`manifest.yaml`](https://coreos.github.io/coreos-assembler/working/#manifestyaml)
An rpm-ostree "manifest" or "treefile", primarily, a list of RPMs and their associated repositories.
See the rpm-ostree documentation for the [treefile format reference](https://coreos.github.io/rpm-ostree/treefile/)
* [`overlay.d/`](https://coreos.github.io/coreos-assembler/working/#overlayd)
A generic way to embed architecture-independent configuration and scripts by creating subdirectories in `overlay.d/`.
Each subdirectory is added to the OSTree commit in lexicographic order.
* [`image.yaml`](https://coreos.github.io/coreos-assembler/working/#imageyaml)
Configuration for the output disk images
Additionally, one may use [`overrides`](https://coreos.github.io/coreos-assembler/working/#using-overrides) to embed local RPMs from the build environment, that should not be pulled from a remote repository:
1. Package the binary as an RPM
2. Add any dependencies of the RPM to `manifest.yaml`
3. Run `cosa fetch` to prepare dependencies
4. Place the RPM in `overrides/rpm`
5. Add the name of your RPM to `manifest.yaml`
6. Run `cosa build`. Your RPM will be added to the final image.
Example: We want to build FCOS with our own kernel
1. Follow [Kernel Building](#kernel-building) to build the kernel
You should end up with at least three RPMs: `kernel`, `kernel-core`, `kernel-modules`.
`kernel` depends on `core` and `modules`, `modules` on `core`, and `core` on common FCOS packages (`bash`, `systemd`, etc.).
These dependencies should already be in the manifest.
2. Run `cosa fetch`
3. Place the kernel RPMs in `overrides/rpm`
`kernel`, `kernel-core`, `kernel-modules` should already be in the manifest (`src/config/manifests/bootable-rpm-ostree.yaml`)
4. Run `cosa build` to create the image
5. Test the image with `cosa run`
6. Run `cosa buildextend-gcp` and `cosa buildextend-azure` to additionaly create a VM image for GCP and Azure
## RPM packaging
If we want to make the most use of CoreOS assembler we should package our applications as RPM packages.
See [creating rpm packages](https://docs.fedoraproject.org/en-US/quick-docs/creating-rpm-packages/).
Brief overview of the required steps:
1. Create a directory with your source code or binary file
2. Add a <package>.spec file
Run the following command to create a spec file template that you can update with information about your package
``` shell
rpmdev-newspec <package>
```
3. Create the RPM
``` shell
fedpkg --release f35 local
```
## Kernel Building
See the [building a custom kernel](https://docs.fedoraproject.org/en-US/quick-docs/kernel/build-custom-kernel/) from the Fedora Project documentation.
The following assumes you are running on a current release of Fedora.
We have a Fedora 35 image available on GCP, make sure you have enough space available and the VM is capable to build the kernel in a reasonable time (e2-standard-8 takes ~2h to finish the build).
1. Install dependencies and clone the kernel
``` shell
sudo dnf install fedpkg fedora-packager rpmdevtools ncurses-devel pesign grubby qt3-devel libXi-devel gcc-c++
fedpkg clone -a kernel && cd kernel
sudo dnf builddep kernel.spec
```
Optionally install `ccache` to speed up rebuilds
``` shell
sudo dnf install ccache
```
2. Check out the kernel branch you want to base your build on
Each release has its own branch. E.g. to customize the kernel for Fedora 35, check out `origin/f35`. `rawhide` tracks the latest iteration, following closely behind the mainline kernel.
``` shell
git checkout origin/f35
git checkout -b custom-kernel
```
3. Customize buildid by chaning `# define buildid .local` to `%define buildid .<your_custom_id_here>` in `kernel.spec`
4. Apply your changes and patches to the kernel
5. Build the RPMs
This will take a while
``` shell
fedpkg local
```
The built kernel RPMs will be in `./x86_64/`
6. You can now use and install the kernel packages
``` shell
sudo dnf install --nogpgcheck ./x86_64/kernel-$version.rpm
```

View file

@ -0,0 +1,45 @@
# Cloud Providers
Custom CoreOS images created here can be uploaded to supported cloud providers. This documents contains information on how to manually spawn cloud provider instances using custom CoreOS images.
## GCP
```shell
gcloud compute instances create <INSTANCE_NAME> --zone=<ZONE> --machine-type=<MACHINE_TYPE> --image <IMAGE_NAME> --maintenance-policy=TERMINATE --confidential-compute --shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/trace.append
```
## Azure
Non-CVM:
```
az image list
# copy image id from output of previous command
az vm create --resource-group <RESOURCE_GROUP> --location <LOCATION> --name <INSTANCE_NAME> --os-type linux --public-ip-sku Standard --image <IMAGE_ID>
```
### Create Marketplace offer
- Upload a vhd and image to azure portal using the Makefile
- Create (or reuse) a `shared image gallery`:
- Create image gallery if it does not exist yet
- Search for "Azure compute galleries" in azure portal
- Click "create"
- Choose "constellation-images" resource group and pick a name, then click create
- Create a VM image definition
- Search for "Azure compute galleries" in azure portal and choose the created gallery
- Click "Create a VM image definition"
- OS type: Linux
- OS state: Generalized
- VM generation: Gen 2
- Publisher: EdgelessSystems
- Offer: constellation-coreos
- SKU: constellation-coreos
- Source image: Choose image uploaded using Makefile
- Create
- Create Marketplace offer (on https://partner.microsoft.com/)
- Navigate to marketplace offers overview (https://partner.microsoft.com/en-us/dashboard/marketplace-offers/overview)
- If you want to create a new version of an existing plan, skip this section
- Click "New offer" -> "Azure Virtual Machine"
- Choose an offer id and alias
- Create a new plan on "Plan overview" -> "Create new plan", choose a plan id and plan name
- In "Technical configuration", create a generation, choose "Azure shared image gallery" and select the image created earlier

0
image/images/.gitkeep Normal file
View file

16
image/podman.env Normal file
View file

@ -0,0 +1,16 @@
cosa() {
env | grep COREOS_ASSEMBLER
local -r COREOS_ASSEMBLER_CONTAINER_LATEST="ghcr.io/edgelesssys/constellation-coreos-assembler:latest"
test -t 1 && USE_TTY="-ti"
set -x
podman run --rm ${USE_TTY} --security-opt label=disable --privileged \
--uidmap=1000:0:1 --uidmap=0:1:1000 --uidmap 1001:1001:64536 \
-v ${PWD}:/srv/ --device /dev/kvm --device /dev/fuse \
--tmpfs /tmp -v /var/tmp:/var/tmp --name constellation-cosa \
${NETRC+-v ${NETRC}:/home/builder/.netrc:ro} \
${COREOS_ASSEMBLER_CONFIG_GIT:+-v $COREOS_ASSEMBLER_CONFIG_GIT:/srv/src/config/:ro} \
${COREOS_ASSEMBLER_GIT:+-v $COREOS_ASSEMBLER_GIT/src/:/usr/lib/coreos-assembler/:ro} \
${COREOS_ASSEMBLER_CONTAINER_RUNTIME_ARGS} \
${COREOS_ASSEMBLER_CONTAINER:-$COREOS_ASSEMBLER_CONTAINER_LATEST} "$@"
rc=$?; set +x; return $rc
}

6
image/runner/README.md Normal file
View file

@ -0,0 +1,6 @@
This folder contains a template for deploying a builder for CoreOS on GCP.
## Manually start a builder instance
```
gcloud compute instances create coreos-builder --enable-nested-virtualization --zone=us-central1-c --boot-disk-size 64GB --machine-type=n2-highmem-4 --image-project="ubuntu-os-cloud" --image="ubuntu-2110-impish-v20220118" --metadata-from-file=user-data=cloud-init.txt
```

View file

@ -0,0 +1,35 @@
#cloud-config
users:
- default
- name: github-actions-runner-user
groups: docker
sudo: ALL=(ALL) NOPASSWD:ALL
package_update: true
packages:
- git
- cryptsetup
- build-essential
- libguestfs-tools
- ca-certificates
- curl
- gnupg
- lsb-release
- jq
- pv
runcmd:
- [sudo, chmod, "+r", "/boot/vmlinuz*"]
- [/bin/bash, -c, "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg"]
- [/bin/bash, -c, "echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" | tee /etc/apt/sources.list.d/docker.list > /dev/null "]
- [apt-get, update]
- [apt-get, install, -y, docker-ce, docker-ce-cli, containerd.io]
- [chmod, 666, /dev/kvm]
- [mkdir, -p, /actions-runner]
- [curl, -o, "/actions-runner/actions-runner-linux-x64-2.286.1.tar.gz", -L, "https://github.com/actions/runner/releases/download/v2.286.1/actions-runner-linux-x64-2.286.1.tar.gz"]
- [/bin/bash, -c, "cd /actions-runner && tar xzf /actions-runner/actions-runner-linux-x64-2.286.1.tar.gz"]
- [chown, -R, github-actions-runner-user:github-actions-runner-user, /actions-runner]
- [sudo, -u, github-actions-runner-user, /bin/bash, -c, "cd /actions-runner && /actions-runner/config.sh --url https://github.com/edgelesssys/constellation-images --ephemeral --labels nested-virt --replace --unattended --token $(curl -u api:$(gcloud secrets versions access latest --secret=constellation-images-coreos-builder-github-token) -X POST -H 'Accept: application/vnd.github.v3+json' https://api.github.com/repos/edgelesssys/constellation-images/actions/runners/registration-token | jq -r .token)"]
- [/bin/bash, -c, "cd /actions-runner && ./svc.sh install"]
- [/bin/bash, -c, "systemctl enable --now actions.runner.edgelesssys-constellation.$(hostname).service"]

View file

@ -0,0 +1,205 @@
import os
import sys
import re
import hmac
import hashlib
import random
import string
import google.cloud.compute_v1 as compute_v1
LABEL="nested-virt"
AUTH_TOKEN_ENV="COREOS_BUILDER_WORKFLOW_FUNCTION_TOKEN"
SA_EMAIL="constellation-cos-builder@constellation-331613.iam.gserviceaccount.com"
SA_SCOPES=[
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/servicecontrol",
"https://www.googleapis.com/auth/cloud-platform",
]
def workflow_job(request):
"""Responds to https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job
Args:
request (flask.Request): HTTP request object.
Returns:
The response text or any set of values that can be turned into a
Response object using
`make_response <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>`.
"""
allow, reason = authorize(request)
if not allow:
return f'unauthorized: {reason}'
request_json = request.get_json()
if request_json and 'action' in request_json:
if request_json['action'] == 'queued':
return job_queued(request_json['workflow_job'])
elif request_json['action'] == 'completed':
return job_completed(request_json['workflow_job'])
elif request_json['action'] == 'in_progress':
return f'nothing to do here'
else:
return f'invalid message format'
def authorize(request) -> (bool, str) :
correct_token = os.environ.get(AUTH_TOKEN_ENV)
if correct_token is None:
return False, 'correct token not set'
correct_hmac = 'sha256=' + hmac.new(correct_token.encode('utf-8'), request.get_data(), hashlib.sha256).hexdigest()
request_hmac = request.headers.get('X-Hub-Signature-256')
if request_hmac is None:
return False, 'X-Hub-Signature-256 not set'
if correct_hmac == request_hmac:
return True, ''
else:
return False, f'X-Hub-Signature-256 incorrect'
def job_queued(workflow_job) -> str:
if not LABEL in workflow_job['labels']:
return f'unexpected job labels: {workflow_job["labels"]}'
cloud_init = generate_cloud_init()
instance_uid = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(6))
try:
create_instance(metadata={'user-data': cloud_init}, instance_name=f'coreos-builder-{instance_uid}')
except Exception as e:
return f'creating instance failed: {e}'
return 'success'
def job_completed(workflow_job) -> str:
if not LABEL in workflow_job['labels']:
return f'unexpected job labels: {workflow_job["labels"]}'
instance_name = workflow_job["runner_name"]
try:
delete_instance(machine_name=instance_name)
except Exception as e:
return f'deleting instance failed: {e}'
return 'success'
def generate_cloud_init() -> str:
with open("cloud-init.txt", "r") as f:
cloud_init = f.read()
return cloud_init
def create_instance(
metadata: dict[str, str],
project_id: str = 'constellation-331613',
zone: str = 'us-central1-c',
instance_name: str = 'coreos-builder',
machine_type: str = "n2-highmem-4",
source_image: str = "projects/ubuntu-os-cloud/global/images/ubuntu-2110-impish-v20220118",
network_name: str = "global/networks/default",
disk_size_gb: int = 64,
enable_nested_virtualization: bool = True,
service_accounts: list[compute_v1.ServiceAccount] = [compute_v1.ServiceAccount(email=SA_EMAIL, scopes=SA_SCOPES)],
) -> compute_v1.Instance:
"""
Send an instance creation request to the Compute Engine API and wait for it to complete.
Args:
project_id: project ID or project number of the Cloud project you want to use.
zone: name of the zone you want to use. For example: us-west3-b
instance_name: name of the new virtual machine.
machine_type: machine type of the VM being created. This value uses the
following format: "zones/{zone}/machineTypes/{type_name}".
For example: "zones/europe-west3-c/machineTypes/f1-micro"
source_image: path to the operating system image to mount on your boot
disk. This can be one of the public images
(like "projects/debian-cloud/global/images/family/debian-10")
or a private image you have access to.
network_name: name of the network you want the new instance to use.
For example: "global/networks/default" represents the `default`
network interface, which is created automatically for each project.
Returns:
Instance object.
"""
instance_client = compute_v1.InstancesClient()
operation_client = compute_v1.ZoneOperationsClient()
# Describe the size and source image of the boot disk to attach to the instance.
disk = compute_v1.AttachedDisk()
initialize_params = compute_v1.AttachedDiskInitializeParams()
initialize_params.source_image = (
source_image
)
initialize_params.disk_size_gb = disk_size_gb
disk.initialize_params = initialize_params
disk.auto_delete = True
disk.boot = True
disk.type_ = "PERSISTENT"
# Use the network interface provided in the network_name argument.
network_interface = compute_v1.NetworkInterface()
network_interface.name = network_name
network_interface.access_configs = [compute_v1.AccessConfig()]
# Collect information into the Instance object.
instance = compute_v1.Instance()
instance.name = instance_name
instance.disks = [disk]
if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
instance.machine_type = machine_type
else:
instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"
instance.network_interfaces = [network_interface]
# Enable nested virtualization if requested
advanced_machine_features = compute_v1.AdvancedMachineFeatures()
advanced_machine_features.enable_nested_virtualization = enable_nested_virtualization
instance.advanced_machine_features = advanced_machine_features
metadata_items = [compute_v1.Items(key=k, value=v) for k, v in metadata.items()]
metadata = compute_v1.Metadata(items=metadata_items)
instance.metadata = metadata
# set service accounts.
instance.service_accounts = service_accounts
# Prepare the request to insert an instance.
request = compute_v1.InsertInstanceRequest()
request.zone = zone
request.project = project_id
request.instance_resource = instance
# Wait for the create operation to complete.
print(f"Creating the {instance_name} instance in {zone}...")
operation = instance_client.insert_unary(request=request)
while operation.status != compute_v1.Operation.Status.DONE:
operation = operation_client.wait(
operation=operation.name, zone=zone, project=project_id
)
if operation.error:
print("Error during creation:", operation.error, file=sys.stderr)
if operation.warnings:
print("Warning during creation:", operation.warnings, file=sys.stderr)
print(f"Instance {instance_name} created.")
return instance
def delete_instance(
project_id: str = 'constellation-331613',
zone: str = 'us-central1-c',
machine_name: str = 'coreos-builder',
) -> None:
"""
Send an instance deletion request to the Compute Engine API and wait for it to complete.
Args:
project_id: project ID or project number of the Cloud project you want to use.
zone: name of the zone you want to use. For example: us-west3-b
machine_name: name of the machine you want to delete.
"""
instance_client = compute_v1.InstancesClient()
operation_client = compute_v1.ZoneOperationsClient()
print(f"Deleting {machine_name} from {zone}...")
operation = instance_client.delete_unary(
project=project_id, zone=zone, instance=machine_name
)
while operation.status != compute_v1.Operation.Status.DONE:
operation = operation_client.wait(
operation=operation.name, zone=zone, project=project_id
)
if operation.error:
print("Error during deletion:", operation.error, file=sys.stderr)
if operation.warnings:
print("Warning during deletion:", operation.warnings, file=sys.stderr)
print(f"Instance {machine_name} deleted.")
return