diff --git a/.env b/.env deleted file mode 120000 index 11d3337e..00000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -conf/environment/default.env \ No newline at end of file diff --git a/.github/workflows/build_and_run.yaml b/.github/workflows/build_and_run.yaml index 024e8600..ff5c53b9 100644 --- a/.github/workflows/build_and_run.yaml +++ b/.github/workflows/build_and_run.yaml @@ -70,11 +70,20 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 - - name: Download build artifacts + + - name: Download build artifact uses: actions/download-artifact@v2 + with: + path: debirf/build + + - name: Setup Ruby and InSpec/Cinc-Auditor + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1 + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + working-directory: server/test + - name: Set up Coinboot requirements - shell: bash - # run: https://raw.githubusercontent.com/frzb/coinboot/"${GITHUB_REF##*/}"/setup_coinboot_requirements | bash run: ./setup_coinboot_requirements - name: Run Coinboot server and boot workers @@ -87,10 +96,10 @@ jobs: export RELEASE=$PRE_RELEASE_TAG ./server/run_coinboot - - name: Download build artifact - uses: actions/download-artifact@v2 - with: - path: debirf/build + #- name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + # with: + # limit-access-to-actor: true - name: Create release on main or develop # Release on develop keeps the type pre-release diff --git a/README.md b/README.md index 5994b4a5..707b6e3e 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ The RootFS (`*initramfs*`) and Kernel (`*vmlinuz*`) you want to use are to be pl #### Plugins -Coinboot plugins should be placed into the directory `./server/plugins` +Coinboot plugins should be placed into the directory `./plugins/enabled` You can create your own plugins (see below) or pick some at: [./plugins](./plugins) diff --git a/coinbootmaker/coinbootmaker b/coinbootmaker/coinbootmaker index 29e57fca..120d5029 100755 --- a/coinbootmaker/coinbootmaker +++ b/coinbootmaker/coinbootmaker @@ -1,7 +1,7 @@ #!/bin/bash set -Eeo pipefail -# Copyright (C) 2018 - 2021 Gunter Miegel coinboot.io +# Copyright (C) 2018 - 2022 Gunter Miegel coinboot.io # # This file is part of Coinboot. # This software may be modified and distributed under the terms @@ -16,22 +16,27 @@ display_help() { echo echo 'Packaged Coinboot pluings are written to the ./builds directory' echo - echo 'Usage: coinbootmaker [-i] [-h] [-l] [-p ]' + echo 'Usage: coinbootmaker [-i] [-h] [-l] [-p ]' echo - echo '-i Interactive mode - opens a shell in the build environment' - echo '-p Plugin to build' - echo '-l List plugins available to build' - echo '-h Display this help' + echo '-i Interactive mode - opens a shell in the build environment' + echo '-p Plugin to build' + echo '-l List plugins available to build' + echo '-h Display this help' echo } list_plugins() { + echo + echo 'Available plugin build scripts' + echo pushd . > /dev/null cd src + find . -type f ! -wholename '*\/upstream*' -name "*.yaml" -printf '%P\n' + popd > /dev/null echo - find * -type f -print + echo 'Usage: ./coinbootmaker -p ' echo - popd > /dev/null + } while getopts "ip:lh" opt; do @@ -58,33 +63,33 @@ done shift $((OPTIND -1)) - WGET='wget --retry-connrefused --waitretry=5 --read-timeout=20 --timeout=15 -t 0' CURL='curl --max-time 5 --retry-max-time 20 --retry 999' CACHE_DIR=$(readlink -f ./cache) GITHUB_REPO=frzb/coinboot -RELEASE=latest +RELEASE=${RELEASE:-latest} ## initramfs and kernel vmlinuz ## # RELEASE is set via an environment variable under ./conf/environment # If the value is 'latest' we determine the latest release, else we use the set value. - - if [ $RELEASE = latest ]; then RESPONSE=$($CURL --silent "https://api.github.com/repos/${GITHUB_REPO}/tags") sleep 5 - while ! TAG=$(echo $RESPONSE | jq -r '.[0].name'); do + while ! TAG=$(echo $RESPONSE | jq -r '[ .[].name | select(test("^pre.*") | not) ] | sort | last'); do echo "Calling the Github API has failed, repeat ..." RESPONSE=$($CURL --silent "https://api.github.com/repos/${GITHUB_REPO}/tags") - sleep 5 - done - echo "Using latest coinboot-debirf release: $TAG" + sleep 5 + done + echo "Coinbootmaker is using the latest (default) Coinboot release: $TAG" +else + TAG=$RELEASE + echo "Coinbootmaker is using Coinboot release: $TAG" fi DOWNLOAD_URL=https://github.com/${GITHUB_REPO}/releases/download/${TAG} if [ -z $KERNEL ]; then - KERNEL=5.4.0-58-generic + KERNEL=5.11.0-46-generic fi INITRAMFS=coinboot-initramfs-$KERNEL @@ -93,33 +98,44 @@ $WGET $DOWNLOAD_URL/$INITRAMFS -P $CACHE_DIR fi BASEDIR=$PWD -#INITRAMFS=$(readlink -f $1) LOWER=/tmp/$(basename $INITRAMFS)_extracted_by_coinbootmaker/lower UPPER=/tmp/$(basename $INITRAMFS)_extracted_by_coinbootmaker/upper WORKING_DIRECTORY=/tmp/$(basename $INITRAMFS)_extracted_by_coinbootmaker/working_dir MERGED=/tmp/$(basename $INITRAMFS)_extracted_by_coinbootmaker/merged -while sudo runc list | grep coinbootmaker | grep running; do +# Initial Cleanup + +while sudo runc list | grep coinbootmaker | grep -q running; do echo 'Waiting for Coinbootmaker container to be stopped ...' sudo runc kill coinbootmaker KILL sleep 1 done -while sudo runc list | grep coinbootmaker | grep stopped; do +while sudo runc list | grep coinbootmaker | grep -q stopped; do echo 'Waiting for Coinbootmaker container to be cleaned up ...' sudo runc delete coinbootmaker sleep 1 done - sudo runc delete coinbootmaker || true - sudo ip link delete cbm-host || true - sudo ip netns delete coinbootmaker || true - if mountpoint $MERGED; then - sudo umount $MERGED - fi - sudo rm -rf $UPPER $LOWER $WORKING_DIRECTORY $MERGED +while sudo ip link | grep -q cbm-host; do + echo 'Waiting for Coinbootmaker network interface to be cleaned up ...' + sudo ip link delete cbm-host sleep 1 +done +while sudo ip netns | grep -q coinbootmaker; do + echo 'Waiting for Coinbootmaker network namespace to be cleaned up ...' + sudo ip netns delete coinbootmaker + sleep 1 +done + +if mountpoint -q $MERGED; then + sudo umount $MERGED +fi + +sudo rm -rf $UPPER $LOWER $WORKING_DIRECTORY $MERGED + +# End of initial Cleanup sudo mkdir -p $UPPER $LOWER $WORKING_DIRECTORY $MERGED # We create our own TMPFS. @@ -136,20 +152,20 @@ cd $LOWER/rootfs # We have to use 'sudo' for 'cpio' else the ownership of the files in the # archive is messed up. # We just extract the nested initramfs archive -zcat $CACHE_DIR/$INITRAMFS | sudo cpio -idvm "rootfs.cgz" -zcat rootfs.cgz | sudo cpio -idm +zstd -d $CACHE_DIR/$INITRAMFS -c | sudo cpio -idm --quiet "rootfs.czst" +zstd -d rootfs.czst -c | sudo cpio -idm --quiet # The nested initramfs archive can be removed now -sudo rm -v rootfs.cgz +sudo rm rootfs.czst # Adapt nameserver settings. # resolv.conf is a symling to the systemd stub resolver which we have to delete beforehand. sudo rm etc/resolv.conf -sudo tee etc/resolv.conf << EOF +sudo tee etc/resolv.conf << EOF 1> /dev/null nameserver 1.1.1.1 EOF -sudo tee etc/hosts << EOF +sudo tee etc/hosts << EOF 1> /dev/null 127.0.1.1 coinbootmaker EOF @@ -162,7 +178,7 @@ cd $LOWER # So we omit the jq limbo and the dependency to jq. # We use the same set of capabilities as Docker by default does. #https://github.com/moby/moby/blob/master/oci/defaults.go#L14-L30 -sudo tee ./config.json << EOF +sudo tee ./config.json << EOF 1> /dev/null { "ociVersion": "1.0.0", "process": { @@ -439,7 +455,7 @@ sudo runc run -d coinbootmaker # This commands can only be executed if the container is already running. # So let's wait until it is ready. -while ! sudo runc list | grep coinbootmaker; do +while ! sudo runc list | grep -q coinbootmaker; do echo 'Waiting for Coinbootmaker container...' sleep 1 done @@ -464,7 +480,7 @@ fi # Cleanup sudo runc kill coinbootmaker KILL -while ! sudo runc list | grep coinbootmaker | grep stopped; do +while ! sudo runc list | grep coinbootmaker | grep -q stopped; do echo 'Waiting for Coinbootmaker container to be stopped ...' sleep 1 done @@ -474,7 +490,7 @@ sudo runc delete coinbootmaker sudo ip link delete cbm-host sudo ip netns delete coinbootmaker -echo "Cleaning up directories" +echo "Cleaning up temporary working directories ..." cd $BASEDIR -sudo umount -v $MERGED -sudo rm -rf $UPPER $LOWER $WORKING_DIRECTORY $MERGED +sudo umount --quiet $MERGED +sudo rm -rf $BASEDIR/plugin $UPPER $LOWER $WORKING_DIRECTORY $MERGED diff --git a/debirf/build_and_run_images b/debirf/build_and_run_images index 62defa7a..f5203655 100755 --- a/debirf/build_and_run_images +++ b/debirf/build_and_run_images @@ -1,6 +1,5 @@ #!/bin/bash set -e -o pipefail -set -x # Copyright (C) 2019 Gunter Miegel coinboot.io # diff --git a/debirf/debirf b/debirf/debirf index 272c60a4..a3c43c8d 100755 --- a/debirf/debirf +++ b/debirf/debirf @@ -380,7 +380,7 @@ else # FIXME: Move this stuff to Python. curl -s http://$HTTP_SERVER/plugins/ | grep -v -Fe '[' -e ']' | cut -f 4 -d'"' | while read plugin; do echo "Downloading and extracting plugin: $plugin" - wget http://$HTTP_SERVER/plugins/$plugin -O - | tar ---no-overwrite -dirPxzvf - + wget http://$HTTP_SERVER/plugins/$plugin -O - | tar --no-overwrite-dir -Pxzvf - /usr/local/bin/dpkg_status.py --new /tmp/dpkg_status --old /var/lib/dpkg/status --union > /tmp/status_$plugin mv -v /tmp/status_$plugin /var/lib/dpkg/status echo '----------------------------' diff --git a/debirf/scripts/create_plugin.py b/debirf/scripts/create_plugin.py index e3890707..b295822e 100755 --- a/debirf/scripts/create_plugin.py +++ b/debirf/scripts/create_plugin.py @@ -138,6 +138,8 @@ def main(arguments): print("------------------------------------") + print("------------------------------------") + print("Created Coinboot Plugin:", archive_name) diff --git a/server/plugins/.keep b/plugins/available/.keep similarity index 100% rename from server/plugins/.keep rename to plugins/available/.keep diff --git a/plugins/enabled/.keep b/plugins/enabled/.keep new file mode 100644 index 00000000..e69de29b diff --git a/server/docker/coinboot-download-helper b/server/docker/coinboot-download-helper index 695f848e..ee30e256 100755 --- a/server/docker/coinboot-download-helper +++ b/server/docker/coinboot-download-helper @@ -33,7 +33,7 @@ GITHUB_REPO=frzb/coinboot if [ $RELEASE = latest ]; then RESPONSE=$($CURL --silent "https://api.github.com/repos/${GITHUB_REPO}/tags") sleep 5 - while ! TAG=$(echo $RESPONSE | jq -r '.[0].name'); do + while ! TAG=$(echo $RESPONSE | jq -r '[ .[].name | select(test("^pre.*") | not) ] | sort | last'); do echo "Calling the Github API has failed, repeat ..." RESPONSE=$($CURL --silent "https://api.github.com/repos/${GITHUB_REPO}/tags") sleep 5 diff --git a/server/plugins b/server/plugins new file mode 120000 index 00000000..97a06e50 --- /dev/null +++ b/server/plugins @@ -0,0 +1 @@ +../plugins/enabled \ No newline at end of file diff --git a/server/run_coinboot b/server/run_coinboot index 4e30e1f5..ed4aeecb 100755 --- a/server/run_coinboot +++ b/server/run_coinboot @@ -18,8 +18,6 @@ set -e -o pipefail # You should have received a copy of the GNU General Public License # along with this program. If not, see . -export RELEASE=$RELEASE_TAG -export VERSION='0.98 Beta' MACHINE_IP='192.168.1.10' COINBOOT_SERVER_IP=192.168.1.2/24 MACHINE_MAC_ADDRESS_BIOS='52:54:04:b9:ab:45' @@ -128,6 +126,36 @@ build_docker_image() { docker build -t coinboot/coinboot:latest ./docker/ } +create_test_plugin() { + echo 'RELEASE is set to :' $RELEASE + pushd . + + cd ../plugins/ + + cat << EOF > src/test-plugin.yaml +--- +plugin: Test plugin +archive_name: test-plugin +version: v0.0.1 +description: Test plugin for verification +maintainer: Gunter Miegel +source: https://github.com/frzb/coinboot +run: | + sudo mkdir /home/ubuntu/test_dir + echo 'This is a test' | sudo tee -a /home/ubuntu/test_dir/file1 + sudo chown -R ubuntu:ubuntu /home/ubuntu/test_dir +EOF + + ./coinbootmaker -p test-plugin.yaml + + pwd + ls -la + + cp -v build/coinboot_test-plugin* enabled/ + + popd +} + up_docker_compose() { docker-compose up -d sleep 30 @@ -155,7 +183,7 @@ wait_for_server_to_be_ready() { } cleanup_virsh_domains() { - # Domains with UEFI need to be deleted with the additional parameter --vram + # Domains with UEFI need to be deleted with the additional parameter --nvram for DOMAIN in $(sudo virsh list --all --name); do if [[ $DOMAIN == *"uefi"* ]]; then sudo virsh undefine --nvram $DOMAIN @@ -200,11 +228,17 @@ verify_and_shutdown_over_ssh() { echo "Waiting $COUNTER second(s) for Coinboot machine to listen on port 22/SSH ..." done - # FIXME: RELEASE does not expanse to date string when 'latest' is specified - while ! sshpass -p ubuntu ssh -o StrictHostKeyChecking=no -l ubuntu -p 22 $MACHINE_IP "cat /etc/motd && lsb_release -a && uname -a && df -m && free -m && zramctl"; do - echo 'Waiting for SSH login to succeed...' - sleep 10 - done + #while ! sshpass -p ubuntu ssh -o StrictHostKeyChecking=no -l ubuntu -p 22 $MACHINE_IP; do + # echo 'Waiting for SSH login to succeed...' + # sleep 10 + #done + + # FIXME: RELEASE does not expanse to date string when 'latest' is specified + #sshpass -p ubuntu ssh -o StrictHostKeyChecking=no -l ubuntu -p 22 $MACHINE_IP + pushd . + cd ./test + bundle exec cinc-auditor exec coinboot_node_spec.rb --user ubuntu --password ubuntu -t ssh://"${MACHINE_IP}" + popd DOMAIN=$(sudo virsh list --name) sudo virsh destroy $DOMAIN @@ -215,7 +249,7 @@ verify_and_shutdown_over_ssh() { sudo apt-get update -sudo apt-get install --yes --no-install-recommends bridge-utils dnsmasq jq sshpass libvirt-daemon-system: virtinst qemu-system-x86 ipxe-qemu ovmf +sudo apt-get install --yes --no-install-recommends bridge-utils dnsmasq jq sshpass libvirt-daemon-system: virtinst qemu-system-x86 ipxe-qemu ovmf zstd # Enable the execution of virsh without root access # mostly used for local debugging and just for Vagrant. @@ -265,12 +299,14 @@ fi # We also need to load the environment before calling docker-compose: # https://github.com/docker/compose/issues/3435 -export $(grep -v '^#' ./conf/environment/* | xargs) +#export $(grep -v '^#' ./conf/environment/* | xargs) # Config syntax check fails because the build environment on Ubuntu focal is # running dnsmasq 2.80 which is lacking "dhcp-ignore-clid", this feature was introduced with 2.81 # check_dnsmasq_config +create_test_plugin + build_docker_image up_docker_compose @@ -288,4 +324,3 @@ run_with_libvirt_uefi verify_and_shutdown_over_ssh $MACHINE_MAC_ADDRESS_UEFI cleanup_virsh_domains - diff --git a/server/test/Gemfile b/server/test/Gemfile new file mode 100644 index 00000000..fdf4bd66 --- /dev/null +++ b/server/test/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" +source "https://packagecloud.io/cinc-project/stable" do + gem "cinc-auditor-bin" , '~> 5.10' +end diff --git a/server/test/coinboot_node_spec.rb b/server/test/coinboot_node_spec.rb new file mode 100644 index 00000000..e843653d --- /dev/null +++ b/server/test/coinboot_node_spec.rb @@ -0,0 +1,60 @@ +# Please always use the cookstyle linter +control 'coinboot-plugin-endpoint' do + impact 1.0 + title 'Coinboot server plugin HTTP endpoint' + desc 'Verify the Coinboot server plugin HTTP endpoint is available' + + describe http('http://192.168.1.2/plugins/') do + its('status') { should cmp 200 } + its('body') { should include 'coinboot_test-plugin_v0.0.1' } + end +end +control 'coinboot-plugin' do + impact 1.0 + title 'Coinboot node plugin file structure' + desc 'Verify plugin file structure on the Coinboot worker node' + + describe directory('/home/ubuntu/test_dir') do + its('owner') { should eq 'ubuntu' } + its('group') { should eq 'ubuntu' } + end + + describe file('/home/ubuntu/test_dir/file1') do + its('owner') { should eq 'ubuntu' } + its('group') { should eq 'ubuntu' } + its('content') { should match 'This is a test' } + end +end + +control 'coinboot-kernel' do + impact 1.0 + title 'Coinboot node Kernel version' + desc 'Verify the Kernel version running on the Coinboot worker node' + + describe command('uname -r') do + its('exit_status') { should eq 0 } + its('stdout') { should include '5.11.0-46-generic' } + end +end + +control 'coinboot-distribution' do + impact 1.0 + title 'Coinboot node Distribution release' + desc 'Verify the distribution release running on the Coinboot worker node' + + describe command('lsb_release -d') do + its('exit_status') { should eq 0 } + its('stdout') { should include 'Description: Ubuntu 20.04.4 LTS' } + end +end + +control 'coinboot-zram' do + impact 1.0 + title 'Coinboot node ZRAM RAM Compresssion' + desc 'Verify the ZSTD compressed ramdrive used for the RootFS' + + describe command('zramctl') do + its('exit_status') { should eq 0 } + its('stdout') { should include '/dev/zram0' } + end +end